Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgssymbollayerv2utils.cpp 00003 --------------------- 00004 begin : November 2009 00005 copyright : (C) 2009 by Martin Dobias 00006 email : wonder.sk at gmail.com 00007 *************************************************************************** 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 ***************************************************************************/ 00015 00016 #include "qgssymbollayerv2utils.h" 00017 00018 #include "qgssymbollayerv2.h" 00019 #include "qgssymbollayerv2registry.h" 00020 #include "qgssymbolv2.h" 00021 #include "qgsvectorcolorrampv2.h" 00022 #include "qgsexpression.h" 00023 00024 #include "qgslogger.h" 00025 #include "qgsrendercontext.h" 00026 00027 #include <QColor> 00028 #include <QFont> 00029 #include <QDomDocument> 00030 #include <QDomNode> 00031 #include <QDomElement> 00032 #include <QIcon> 00033 #include <QPainter> 00034 00035 QString QgsSymbolLayerV2Utils::encodeColor( QColor color ) 00036 { 00037 return QString( "%1,%2,%3,%4" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ).arg( color.alpha() ); 00038 } 00039 00040 QColor QgsSymbolLayerV2Utils::decodeColor( QString str ) 00041 { 00042 QStringList lst = str.split( "," ); 00043 if ( lst.count() < 3 ) 00044 { 00045 return QColor(); 00046 } 00047 int red, green, blue, alpha; 00048 red = lst[0].toInt(); 00049 green = lst[1].toInt(); 00050 blue = lst[2].toInt(); 00051 alpha = 255; 00052 if ( lst.count() > 3 ) 00053 { 00054 alpha = lst[3].toInt(); 00055 } 00056 return QColor( red, green, blue, alpha ); 00057 } 00058 00059 QString QgsSymbolLayerV2Utils::encodeSldAlpha( int alpha ) 00060 { 00061 return QString::number( alpha / 255.0, 'f', 2 ); 00062 } 00063 00064 int QgsSymbolLayerV2Utils::decodeSldAlpha( QString str ) 00065 { 00066 bool ok; 00067 double alpha = str.toDouble( &ok ); 00068 if ( !ok || alpha > 1 ) 00069 alpha = 255; 00070 else if ( alpha < 0 ) 00071 alpha = 0; 00072 return alpha * 255; 00073 } 00074 00075 QString QgsSymbolLayerV2Utils::encodeSldFontStyle( QFont::Style style ) 00076 { 00077 switch ( style ) 00078 { 00079 case QFont::StyleNormal: return "normal"; 00080 case QFont::StyleItalic: return "italic"; 00081 case QFont::StyleOblique: return "oblique"; 00082 default: return ""; 00083 } 00084 } 00085 00086 QFont::Style QgsSymbolLayerV2Utils::decodeSldFontStyle( QString str ) 00087 { 00088 if ( str == "normal" ) return QFont::StyleNormal; 00089 if ( str == "italic" ) return QFont::StyleItalic; 00090 if ( str == "oblique" ) return QFont::StyleOblique; 00091 return QFont::StyleNormal; 00092 } 00093 00094 QString QgsSymbolLayerV2Utils::encodeSldFontWeight( int weight ) 00095 { 00096 if ( weight == 50 ) return "normal"; 00097 if ( weight == 75 ) return "bold"; 00098 00099 // QFont::Weight is between 0 and 99 00100 // CSS font-weight is between 100 and 900 00101 if ( weight < 0 ) return "100"; 00102 if ( weight > 99 ) return "900"; 00103 return QString::number( weight * 800 / 99 + 100 ); 00104 } 00105 00106 int QgsSymbolLayerV2Utils::decodeSldFontWeight( QString str ) 00107 { 00108 bool ok; 00109 int weight = str.toInt( &ok ); 00110 if ( !ok ) return ( int ) QFont::Normal; 00111 00112 // CSS font-weight is between 100 and 900 00113 // QFont::Weight is between 0 and 99 00114 if ( weight > 900 ) return 99; 00115 if ( weight < 100 ) return 0; 00116 return ( weight - 100 ) * 99 / 800; 00117 } 00118 00119 QString QgsSymbolLayerV2Utils::encodePenStyle( Qt::PenStyle style ) 00120 { 00121 switch ( style ) 00122 { 00123 case Qt::NoPen: return "no"; 00124 case Qt::SolidLine: return "solid"; 00125 case Qt::DashLine: return "dash"; 00126 case Qt::DotLine: return "dot"; 00127 case Qt::DashDotLine: return "dash dot"; 00128 case Qt::DashDotDotLine: return "dash dot dot"; 00129 default: return "???"; 00130 } 00131 } 00132 00133 Qt::PenStyle QgsSymbolLayerV2Utils::decodePenStyle( QString str ) 00134 { 00135 if ( str == "no" ) return Qt::NoPen; 00136 if ( str == "solid" ) return Qt::SolidLine; 00137 if ( str == "dash" ) return Qt::DashLine; 00138 if ( str == "dot" ) return Qt::DotLine; 00139 if ( str == "dash dot" ) return Qt::DashDotLine; 00140 if ( str == "dash dot dot" ) return Qt::DashDotDotLine; 00141 return Qt::SolidLine; 00142 } 00143 00144 QString QgsSymbolLayerV2Utils::encodePenJoinStyle( Qt::PenJoinStyle style ) 00145 { 00146 switch ( style ) 00147 { 00148 case Qt::BevelJoin: return "bevel"; 00149 case Qt::MiterJoin: return "miter"; 00150 case Qt::RoundJoin: return "round"; 00151 default: return "???"; 00152 } 00153 } 00154 00155 Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodePenJoinStyle( QString str ) 00156 { 00157 if ( str == "bevel" ) return Qt::BevelJoin; 00158 if ( str == "miter" ) return Qt::MiterJoin; 00159 if ( str == "round" ) return Qt::RoundJoin; 00160 return Qt::BevelJoin; 00161 } 00162 00163 QString QgsSymbolLayerV2Utils::encodeSldLineJoinStyle( Qt::PenJoinStyle style ) 00164 { 00165 switch ( style ) 00166 { 00167 case Qt::BevelJoin: return "bevel"; 00168 case Qt::MiterJoin: return "mitre"; 00169 case Qt::RoundJoin: return "round"; 00170 default: return ""; 00171 } 00172 } 00173 00174 Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodeSldLineJoinStyle( QString str ) 00175 { 00176 if ( str == "bevel" ) return Qt::BevelJoin; 00177 if ( str == "mitre" ) return Qt::MiterJoin; 00178 if ( str == "round" ) return Qt::RoundJoin; 00179 return Qt::BevelJoin; 00180 } 00181 00182 QString QgsSymbolLayerV2Utils::encodePenCapStyle( Qt::PenCapStyle style ) 00183 { 00184 switch ( style ) 00185 { 00186 case Qt::SquareCap: return "square"; 00187 case Qt::FlatCap: return "flat"; 00188 case Qt::RoundCap: return "round"; 00189 default: return "???"; 00190 } 00191 } 00192 00193 Qt::PenCapStyle QgsSymbolLayerV2Utils::decodePenCapStyle( QString str ) 00194 { 00195 if ( str == "square" ) return Qt::SquareCap; 00196 if ( str == "flat" ) return Qt::FlatCap; 00197 if ( str == "round" ) return Qt::RoundCap; 00198 return Qt::SquareCap; 00199 } 00200 00201 QString QgsSymbolLayerV2Utils::encodeSldLineCapStyle( Qt::PenCapStyle style ) 00202 { 00203 switch ( style ) 00204 { 00205 case Qt::SquareCap: return "square"; 00206 case Qt::FlatCap: return "butt"; 00207 case Qt::RoundCap: return "round"; 00208 default: return ""; 00209 } 00210 } 00211 00212 Qt::PenCapStyle QgsSymbolLayerV2Utils::decodeSldLineCapStyle( QString str ) 00213 { 00214 if ( str == "square" ) return Qt::SquareCap; 00215 if ( str == "butt" ) return Qt::FlatCap; 00216 if ( str == "round" ) return Qt::RoundCap; 00217 return Qt::SquareCap; 00218 } 00219 00220 QString QgsSymbolLayerV2Utils::encodeBrushStyle( Qt::BrushStyle style ) 00221 { 00222 switch ( style ) 00223 { 00224 case Qt::SolidPattern : return "solid"; 00225 case Qt::HorPattern : return "horizontal"; 00226 case Qt::VerPattern : return "vertical"; 00227 case Qt::CrossPattern : return "cross"; 00228 case Qt::BDiagPattern : return "b_diagonal"; 00229 case Qt::FDiagPattern : return "f_diagonal"; 00230 case Qt::DiagCrossPattern : return "diagonal_x"; 00231 case Qt::Dense1Pattern : return "dense1"; 00232 case Qt::Dense2Pattern : return "dense2"; 00233 case Qt::Dense3Pattern : return "dense3"; 00234 case Qt::Dense4Pattern : return "dense4"; 00235 case Qt::Dense5Pattern : return "dense5"; 00236 case Qt::Dense6Pattern : return "dense6"; 00237 case Qt::Dense7Pattern : return "dense7"; 00238 case Qt::NoBrush : return "no"; 00239 default: return "???"; 00240 } 00241 } 00242 00243 Qt::BrushStyle QgsSymbolLayerV2Utils::decodeBrushStyle( QString str ) 00244 { 00245 if ( str == "solid" ) return Qt::SolidPattern; 00246 if ( str == "horizontal" ) return Qt::HorPattern; 00247 if ( str == "vertical" ) return Qt::VerPattern; 00248 if ( str == "cross" ) return Qt::CrossPattern; 00249 if ( str == "b_diagonal" ) return Qt::BDiagPattern; 00250 if ( str == "f_diagonal" ) return Qt::FDiagPattern; 00251 if ( str == "diagonal_x" ) return Qt::DiagCrossPattern; 00252 if ( str == "dense1" ) return Qt::Dense1Pattern; 00253 if ( str == "dense2" ) return Qt::Dense2Pattern; 00254 if ( str == "dense3" ) return Qt::Dense3Pattern; 00255 if ( str == "dense4" ) return Qt::Dense4Pattern; 00256 if ( str == "dense5" ) return Qt::Dense5Pattern; 00257 if ( str == "dense6" ) return Qt::Dense6Pattern; 00258 if ( str == "dense7" ) return Qt::Dense7Pattern; 00259 if ( str == "no" ) return Qt::NoBrush; 00260 return Qt::SolidPattern; 00261 } 00262 00263 QString QgsSymbolLayerV2Utils::encodeSldBrushStyle( Qt::BrushStyle style ) 00264 { 00265 switch ( style ) 00266 { 00267 case Qt::CrossPattern: return "cross"; 00268 case Qt::DiagCrossPattern: return "x"; 00269 00270 /* The following names are taken from the presentation "GeoServer 00271 * Cartographic Rendering" by Andrea Aime at the FOSS4G 2010. 00272 * (see http://2010.foss4g.org/presentations/3588.pdf) 00273 */ 00274 case Qt::HorPattern: return "horline"; 00275 case Qt::VerPattern: return "line"; 00276 case Qt::BDiagPattern: return "slash"; 00277 case Qt::FDiagPattern: return "backslash"; 00278 00279 /* define the other names following the same pattern used above */ 00280 case Qt::Dense1Pattern: 00281 case Qt::Dense2Pattern: 00282 case Qt::Dense3Pattern: 00283 case Qt::Dense4Pattern: 00284 case Qt::Dense5Pattern: 00285 case Qt::Dense6Pattern: 00286 case Qt::Dense7Pattern: 00287 return QString( "brush://%1" ).arg( encodeBrushStyle( style ) ); 00288 00289 default: 00290 return QString(); 00291 } 00292 } 00293 00294 Qt::BrushStyle QgsSymbolLayerV2Utils::decodeSldBrushStyle( QString str ) 00295 { 00296 if ( str == "horline" ) return Qt::HorPattern; 00297 if ( str == "line" ) return Qt::VerPattern; 00298 if ( str == "cross" ) return Qt::CrossPattern; 00299 if ( str == "slash" ) return Qt::BDiagPattern; 00300 if ( str == "backshash" ) return Qt::FDiagPattern; 00301 if ( str == "x" ) return Qt::DiagCrossPattern; 00302 00303 if ( str.startsWith( "brush://" ) ) 00304 return decodeBrushStyle( str.mid( 8 ) ); 00305 00306 return Qt::NoBrush; 00307 } 00308 00309 QString QgsSymbolLayerV2Utils::encodePoint( QPointF point ) 00310 { 00311 return QString( "%1,%2" ).arg( point.x() ).arg( point.y() ); 00312 } 00313 00314 QPointF QgsSymbolLayerV2Utils::decodePoint( QString str ) 00315 { 00316 QStringList lst = str.split( ',' ); 00317 if ( lst.count() != 2 ) 00318 return QPointF( 0, 0 ); 00319 return QPointF( lst[0].toDouble(), lst[1].toDouble() ); 00320 } 00321 00322 QString QgsSymbolLayerV2Utils::encodeOutputUnit( QgsSymbolV2::OutputUnit unit ) 00323 { 00324 switch ( unit ) 00325 { 00326 case QgsSymbolV2::MM: 00327 return "MM"; 00328 case QgsSymbolV2::MapUnit: 00329 return "MapUnit"; 00330 default: 00331 return "MM"; 00332 } 00333 } 00334 00335 QgsSymbolV2::OutputUnit QgsSymbolLayerV2Utils::decodeOutputUnit( QString str ) 00336 { 00337 if ( str == "MM" ) 00338 { 00339 return QgsSymbolV2::MM; 00340 } 00341 else if ( str == "MapUnit" ) 00342 { 00343 return QgsSymbolV2::MapUnit; 00344 } 00345 00346 // milimeters are default 00347 return QgsSymbolV2::MM; 00348 } 00349 00350 QString QgsSymbolLayerV2Utils::encodeSldUom( QgsSymbolV2::OutputUnit unit, double *scaleFactor ) 00351 { 00352 switch ( unit ) 00353 { 00354 case QgsSymbolV2::MapUnit: 00355 if ( scaleFactor ) 00356 *scaleFactor = 0.001; // from millimeters to meters 00357 return "http://www.opengeospatial.org/se/units/metre"; 00358 00359 case QgsSymbolV2::MM: 00360 default: 00361 // pixel is the SLD default uom. The "standardized rendering pixel 00362 // size" is defined to be 0.28mm × 0.28mm (millimeters). 00363 if ( scaleFactor ) 00364 *scaleFactor = 0.28; // from millimeters to pixels 00365 00366 // http://www.opengeospatial.org/sld/units/pixel 00367 return QString(); 00368 } 00369 } 00370 00371 QgsSymbolV2::OutputUnit QgsSymbolLayerV2Utils::decodeSldUom( QString str, double *scaleFactor ) 00372 { 00373 if ( str == "http://www.opengeospatial.org/se/units/metre" ) 00374 { 00375 if ( scaleFactor ) 00376 *scaleFactor = 1000.0; // from meters to millimeters 00377 return QgsSymbolV2::MapUnit; 00378 } 00379 else if ( str == "http://www.opengeospatial.org/se/units/foot" ) 00380 { 00381 if ( scaleFactor ) 00382 *scaleFactor = 304.8; // from feet to meters 00383 return QgsSymbolV2::MapUnit; 00384 } 00385 00386 // pixel is the SLD default uom. The "standardized rendering pixel 00387 // size" is defined to be 0.28mm x 0.28mm (millimeters). 00388 if ( scaleFactor ) 00389 *scaleFactor = 1 / 0.00028; // from pixels to millimeters 00390 return QgsSymbolV2::MM; 00391 } 00392 00393 QString QgsSymbolLayerV2Utils::encodeRealVector( const QVector<qreal>& v ) 00394 { 00395 QString vectorString; 00396 QVector<qreal>::const_iterator it = v.constBegin(); 00397 for ( ; it != v.constEnd(); ++it ) 00398 { 00399 if ( it != v.constBegin() ) 00400 { 00401 vectorString.append( ";" ); 00402 } 00403 vectorString.append( QString::number( *it ) ); 00404 } 00405 return vectorString; 00406 } 00407 00408 QVector<qreal> QgsSymbolLayerV2Utils::decodeRealVector( const QString& s ) 00409 { 00410 QVector<qreal> resultVector; 00411 00412 QStringList realList = s.split( ";" ); 00413 QStringList::const_iterator it = realList.constBegin(); 00414 for ( ; it != realList.constEnd(); ++it ) 00415 { 00416 resultVector.append( it->toDouble() ); 00417 } 00418 00419 return resultVector; 00420 } 00421 00422 QString QgsSymbolLayerV2Utils::encodeSldRealVector( const QVector<qreal>& v ) 00423 { 00424 QString vectorString; 00425 QVector<qreal>::const_iterator it = v.constBegin(); 00426 for ( ; it != v.constEnd(); ++it ) 00427 { 00428 if ( it != v.constBegin() ) 00429 { 00430 vectorString.append( " " ); 00431 } 00432 vectorString.append( QString::number( *it ) ); 00433 } 00434 return vectorString; 00435 } 00436 00437 QVector<qreal> QgsSymbolLayerV2Utils::decodeSldRealVector( const QString& s ) 00438 { 00439 QVector<qreal> resultVector; 00440 00441 QStringList realList = s.split( " " ); 00442 QStringList::const_iterator it = realList.constBegin(); 00443 for ( ; it != realList.constEnd(); ++it ) 00444 { 00445 resultVector.append( it->toDouble() ); 00446 } 00447 00448 return resultVector; 00449 } 00450 00451 QIcon QgsSymbolLayerV2Utils::symbolPreviewIcon( QgsSymbolV2* symbol, QSize size ) 00452 { 00453 return QIcon( symbolPreviewPixmap( symbol, size ) ); 00454 } 00455 00456 QPixmap QgsSymbolLayerV2Utils::symbolPreviewPixmap( QgsSymbolV2* symbol, QSize size ) 00457 { 00458 Q_ASSERT( symbol ); 00459 00460 QPixmap pixmap( size ); 00461 pixmap.fill( Qt::transparent ); 00462 QPainter painter; 00463 painter.begin( &pixmap ); 00464 painter.setRenderHint( QPainter::Antialiasing ); 00465 symbol->drawPreviewIcon( &painter, size ); 00466 painter.end(); 00467 return pixmap; 00468 } 00469 00470 00471 QIcon QgsSymbolLayerV2Utils::symbolLayerPreviewIcon( QgsSymbolLayerV2* layer, QgsSymbolV2::OutputUnit u, QSize size ) 00472 { 00473 QPixmap pixmap( size ); 00474 pixmap.fill( Qt::transparent ); 00475 QPainter painter; 00476 painter.begin( &pixmap ); 00477 painter.setRenderHint( QPainter::Antialiasing ); 00478 QgsRenderContext renderContext = createRenderContext( &painter ); 00479 QgsSymbolV2RenderContext symbolContext( renderContext, u ); 00480 layer->drawPreviewIcon( symbolContext, size ); 00481 painter.end(); 00482 return QIcon( pixmap ); 00483 } 00484 00485 QIcon QgsSymbolLayerV2Utils::colorRampPreviewIcon( QgsVectorColorRampV2* ramp, QSize size ) 00486 { 00487 return QIcon( colorRampPreviewPixmap( ramp, size ) ); 00488 } 00489 00490 QPixmap QgsSymbolLayerV2Utils::colorRampPreviewPixmap( QgsVectorColorRampV2* ramp, QSize size ) 00491 { 00492 QPixmap pixmap( size ); 00493 QPainter painter; 00494 painter.begin( &pixmap ); 00495 painter.setRenderHint( QPainter::Antialiasing ); 00496 painter.eraseRect( QRect( QPoint( 0, 0 ), size ) ); 00497 for ( int i = 0; i < size.width(); i++ ) 00498 { 00499 QPen pen( ramp->color(( double ) i / size.width() ) ); 00500 painter.setPen( pen ); 00501 painter.drawLine( i, 0, i, size.height() - 1 ); 00502 } 00503 painter.end(); 00504 return pixmap; 00505 } 00506 00507 00508 #include <QPolygonF> 00509 00510 #include <cmath> 00511 #include <cfloat> 00512 00513 00514 // calculate line's angle and tangent 00515 static bool lineInfo( QPointF p1, QPointF p2, double& angle, double& t ) 00516 { 00517 double x1 = p1.x(), y1 = p1.y(), x2 = p2.x(), y2 = p2.y(); 00518 00519 if ( x1 == x2 && y1 == y2 ) 00520 return false; 00521 00522 // tangent 00523 t = ( x1 == x2 ? DBL_MAX : ( y2 - y1 ) / ( x2 - x1 ) ); 00524 00525 // angle 00526 if ( t == DBL_MAX ) 00527 angle = ( y2 > y1 ? M_PI / 2 : M_PI * 3 / 2 ); // angle is 90 or 270 00528 else if ( t == 0 ) 00529 angle = ( x2 > x1 ? 0 : M_PI ); // angle is 0 or 180 00530 else if ( t >= 0 ) 00531 angle = ( y2 > y1 ? atan( t ) : M_PI + atan( t ) ); 00532 else // t < 0 00533 angle = ( y2 > y1 ? M_PI + atan( t ) : atan( t ) ); 00534 00535 return true; 00536 } 00537 00538 // offset a point with an angle and distance 00539 static QPointF offsetPoint( QPointF pt, double angle, double dist ) 00540 { 00541 return QPointF( pt.x() + dist * cos( angle ), pt.y() + dist * sin( angle ) ); 00542 } 00543 00544 // calc intersection of two (infinite) lines defined by one point and tangent 00545 static QPointF linesIntersection( QPointF p1, double t1, QPointF p2, double t2 ) 00546 { 00547 // parallel lines? (or the difference between angles is less than cca 0.1 degree) 00548 if (( t1 == DBL_MAX && t2 == DBL_MAX ) || qAbs( t1 - t2 ) < 0.001 ) 00549 return QPointF(); 00550 00551 double x, y; 00552 if ( t1 == DBL_MAX || t2 == DBL_MAX ) 00553 { 00554 // in case one line is with angle 90 resp. 270 degrees (tangent undefined) 00555 // swap them so that line 2 is with undefined tangent 00556 if ( t1 == DBL_MAX ) 00557 { 00558 QPointF pSwp = p1; p1 = p2; p2 = pSwp; 00559 double tSwp = t1; t1 = t2; t2 = tSwp; 00560 } 00561 00562 x = p2.x(); 00563 } 00564 else 00565 { 00566 // usual case 00567 x = (( p1.y() - p2.y() ) + t2 * p2.x() - t1 * p1.x() ) / ( t2 - t1 ); 00568 } 00569 00570 y = p1.y() + t1 * ( x - p1.x() ); 00571 return QPointF( x, y ); 00572 } 00573 00574 00575 QPolygonF offsetLine( QPolygonF polyline, double dist ) 00576 { 00577 QPolygonF newLine; 00578 00579 if ( polyline.count() < 2 ) 00580 return newLine; 00581 00582 double angle = 0.0, t_new, t_old = 0; 00583 QPointF pt_old, pt_new; 00584 QPointF p1 = polyline[0], p2; 00585 bool first_point = true; 00586 00587 for ( int i = 1; i < polyline.count(); i++ ) 00588 { 00589 p2 = polyline[i]; 00590 00591 if ( !lineInfo( p1, p2, angle, t_new ) ) 00592 continue; // not a line... 00593 00594 pt_new = offsetPoint( p1, angle + M_PI / 2, dist ); 00595 00596 if ( ! first_point ) 00597 { 00598 // if it's not the first line segment 00599 // calc intersection with last line (with offset) 00600 QPointF pt_tmp = linesIntersection( pt_old, t_old, pt_new, t_new ); 00601 if ( !pt_tmp.isNull() ) 00602 pt_new = pt_tmp; 00603 } 00604 00605 newLine.append( pt_new ); 00606 00607 pt_old = pt_new; 00608 t_old = t_new; 00609 p1 = p2; 00610 first_point = false; 00611 } 00612 00613 // last line segment: 00614 pt_new = offsetPoint( p2, angle + M_PI / 2, dist ); 00615 newLine.append( pt_new ); 00616 return newLine; 00617 } 00618 00620 00621 00622 QgsSymbolV2* QgsSymbolLayerV2Utils::loadSymbol( QDomElement& element ) 00623 { 00624 QgsSymbolLayerV2List layers; 00625 QDomNode layerNode = element.firstChild(); 00626 00627 while ( !layerNode.isNull() ) 00628 { 00629 QDomElement e = layerNode.toElement(); 00630 if ( !e.isNull() ) 00631 { 00632 if ( e.tagName() != "layer" ) 00633 { 00634 QgsDebugMsg( "unknown tag " + e.tagName() ); 00635 } 00636 else 00637 { 00638 QgsSymbolLayerV2* layer = loadSymbolLayer( e ); 00639 if ( layer != NULL ) 00640 layers.append( layer ); 00641 } 00642 } 00643 layerNode = layerNode.nextSibling(); 00644 } 00645 00646 if ( layers.count() == 0 ) 00647 { 00648 QgsDebugMsg( "no layers for symbol" ); 00649 return NULL; 00650 } 00651 00652 QString symbolType = element.attribute( "type" ); 00653 00654 QgsSymbolV2* symbol = 0; 00655 if ( symbolType == "line" ) 00656 symbol = new QgsLineSymbolV2( layers ); 00657 else if ( symbolType == "fill" ) 00658 symbol = new QgsFillSymbolV2( layers ); 00659 else if ( symbolType == "marker" ) 00660 symbol = new QgsMarkerSymbolV2( layers ); 00661 else 00662 { 00663 QgsDebugMsg( "unknown symbol type " + symbolType ); 00664 return NULL; 00665 } 00666 00667 symbol->setOutputUnit( decodeOutputUnit( element.attribute( "outputUnit" ) ) ); 00668 symbol->setAlpha( element.attribute( "alpha", "1.0" ).toDouble() ); 00669 00670 return symbol; 00671 } 00672 00673 QgsSymbolLayerV2* QgsSymbolLayerV2Utils::loadSymbolLayer( QDomElement& element ) 00674 { 00675 QString layerClass = element.attribute( "class" ); 00676 bool locked = element.attribute( "locked" ).toInt(); 00677 int pass = element.attribute( "pass" ).toInt(); 00678 00679 // parse properties 00680 QgsStringMap props = parseProperties( element ); 00681 00682 QgsSymbolLayerV2* layer; 00683 layer = QgsSymbolLayerV2Registry::instance()->createSymbolLayer( layerClass, props ); 00684 if ( layer ) 00685 { 00686 layer->setLocked( locked ); 00687 layer->setRenderingPass( pass ); 00688 return layer; 00689 } 00690 else 00691 { 00692 QgsDebugMsg( "unknown class " + layerClass ); 00693 return NULL; 00694 } 00695 } 00696 00697 static QString _nameForSymbolType( QgsSymbolV2::SymbolType type ) 00698 { 00699 switch ( type ) 00700 { 00701 case QgsSymbolV2::Line: return "line"; 00702 case QgsSymbolV2::Marker: return "marker"; 00703 case QgsSymbolV2::Fill: return "fill"; 00704 default: return ""; 00705 } 00706 } 00707 00708 QDomElement QgsSymbolLayerV2Utils::saveSymbol( QString name, QgsSymbolV2* symbol, QDomDocument& doc, QgsSymbolV2Map* subSymbols ) 00709 { 00710 Q_ASSERT( symbol ); 00711 00712 QDomElement symEl = doc.createElement( "symbol" ); 00713 symEl.setAttribute( "type", _nameForSymbolType( symbol->type() ) ); 00714 symEl.setAttribute( "name", name ); 00715 symEl.setAttribute( "outputUnit", encodeOutputUnit( symbol->outputUnit() ) ); 00716 symEl.setAttribute( "alpha", QString::number( symbol->alpha() ) ); 00717 QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) ); 00718 for ( int i = 0; i < symbol->symbolLayerCount(); i++ ) 00719 { 00720 QgsSymbolLayerV2* layer = symbol->symbolLayer( i ); 00721 00722 QDomElement layerEl = doc.createElement( "layer" ); 00723 layerEl.setAttribute( "class", layer->layerType() ); 00724 layerEl.setAttribute( "locked", layer->isLocked() ); 00725 layerEl.setAttribute( "pass", layer->renderingPass() ); 00726 00727 if ( subSymbols != NULL && layer->subSymbol() != NULL ) 00728 { 00729 QString subname = QString( "@%1@%2" ).arg( name ).arg( i ); 00730 subSymbols->insert( subname, layer->subSymbol() ); 00731 } 00732 00733 saveProperties( layer->properties(), doc, layerEl ); 00734 symEl.appendChild( layerEl ); 00735 } 00736 00737 return symEl; 00738 } 00739 00740 00741 bool QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( QDomElement& element, 00742 QGis::GeometryType geomType, 00743 QgsSymbolLayerV2List &layers ) 00744 { 00745 QgsDebugMsg( "Entered." ); 00746 00747 if ( element.isNull() ) 00748 return false; 00749 00750 QgsSymbolLayerV2 *l = 0; 00751 00752 QString symbolizerName = element.localName(); 00753 00754 if ( symbolizerName == "PointSymbolizer" ) 00755 { 00756 // first check for Graphic element, nothing will be rendered if not found 00757 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00758 if ( graphicElem.isNull() ) 00759 { 00760 QgsDebugMsg( "Graphic element not found in PointSymbolizer" ); 00761 } 00762 else 00763 { 00764 switch ( geomType ) 00765 { 00766 case QGis::Polygon: 00767 // polygon layer and point symbolizer: draw poligon centroid 00768 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "CentroidFill", element ); 00769 if ( l ) 00770 layers.append( l ); 00771 00772 break; 00773 00774 case QGis::Point: 00775 // point layer and point symbolizer: use markers 00776 l = createMarkerLayerFromSld( element ); 00777 if ( l ) 00778 layers.append( l ); 00779 00780 break; 00781 00782 case QGis::Line: 00783 // line layer and point symbolizer: draw central point 00784 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element ); 00785 if ( l ) 00786 layers.append( l ); 00787 00788 break; 00789 00790 default: 00791 break; 00792 } 00793 } 00794 } 00795 00796 if ( symbolizerName == "LineSymbolizer" ) 00797 { 00798 // check for Stroke element, nothing will be rendered if not found 00799 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 00800 if ( strokeElem.isNull() ) 00801 { 00802 QgsDebugMsg( "Stroke element not found in LineSymbolizer" ); 00803 } 00804 else 00805 { 00806 switch ( geomType ) 00807 { 00808 case QGis::Polygon: 00809 case QGis::Line: 00810 // polygon layer and line symbolizer: draw polygon outline 00811 // line layer and line symbolizer: draw line 00812 l = createLineLayerFromSld( element ); 00813 if ( l ) 00814 layers.append( l ); 00815 00816 break; 00817 00818 case QGis::Point: 00819 // point layer and line symbolizer: draw a little line marker 00820 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element ); 00821 if ( l ) 00822 layers.append( l ); 00823 00824 default: 00825 break; 00826 } 00827 } 00828 } 00829 00830 if ( symbolizerName == "PolygonSymbolizer" ) 00831 { 00832 // get Fill and Stroke elements, nothing will be rendered if both are missing 00833 QDomElement fillElem = element.firstChildElement( "Fill" ); 00834 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 00835 if ( fillElem.isNull() && strokeElem.isNull() ) 00836 { 00837 QgsDebugMsg( "neither Fill nor Stroke element not found in PolygonSymbolizer" ); 00838 } 00839 else 00840 { 00841 QgsSymbolLayerV2 *l = 0; 00842 00843 switch ( geomType ) 00844 { 00845 case QGis::Polygon: 00846 // polygon layer and polygon symbolizer: draw fill 00847 00848 l = createFillLayerFromSld( element ); 00849 if ( l ) 00850 { 00851 layers.append( l ); 00852 00853 // SVGFill and SimpleFill symbolLayerV2 supports outline internally, 00854 // so don't go forward to create a different symbolLayerV2 for outline 00855 if ( l->layerType() == "SimpleFill" || l->layerType() == "SVGFill" ) 00856 break; 00857 } 00858 00859 // now create polygon outline 00860 // polygon layer and polygon symbolizer: draw polygon outline 00861 l = createLineLayerFromSld( element ); 00862 if ( l ) 00863 layers.append( l ); 00864 00865 break; 00866 00867 case QGis::Line: 00868 // line layer and polygon symbolizer: draw line 00869 l = createLineLayerFromSld( element ); 00870 if ( l ) 00871 layers.append( l ); 00872 00873 break; 00874 00875 case QGis::Point: 00876 // point layer and polygon symbolizer: draw a square marker 00877 convertPolygonSymbolizerToPointMarker( element, layers ); 00878 break; 00879 00880 default: 00881 break; 00882 } 00883 } 00884 } 00885 00886 return true; 00887 } 00888 00889 QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createFillLayerFromSld( QDomElement &element ) 00890 { 00891 QDomElement fillElem = element.firstChildElement( "Fill" ); 00892 if ( fillElem.isNull() ) 00893 { 00894 QgsDebugMsg( "Fill element not found" ); 00895 return NULL; 00896 } 00897 00898 QgsSymbolLayerV2 *l = 0; 00899 00900 if ( needLinePatternFill( element ) ) 00901 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "LinePatternFill", element ); 00902 else if ( needPointPatternFill( element ) ) 00903 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "PointPatternFill", element ); 00904 else if ( needSvgFill( element ) ) 00905 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SVGFill", element ); 00906 else 00907 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleFill", element ); 00908 00909 return l; 00910 } 00911 00912 QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createLineLayerFromSld( QDomElement &element ) 00913 { 00914 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 00915 if ( strokeElem.isNull() ) 00916 { 00917 QgsDebugMsg( "Stroke element not found" ); 00918 return NULL; 00919 } 00920 00921 QgsSymbolLayerV2 *l = 0; 00922 00923 if ( needMarkerLine( element ) ) 00924 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element ); 00925 else 00926 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleLine", element ); 00927 00928 return l; 00929 } 00930 00931 QgsSymbolLayerV2* QgsSymbolLayerV2Utils::createMarkerLayerFromSld( QDomElement &element ) 00932 { 00933 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00934 if ( graphicElem.isNull() ) 00935 { 00936 QgsDebugMsg( "Graphic element not found" ); 00937 return NULL; 00938 } 00939 00940 QgsSymbolLayerV2 *l = 0; 00941 00942 if ( needFontMarker( element ) ) 00943 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "FontMarker", element ); 00944 else if ( needSvgMarker( element ) ) 00945 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SvgMarker", element ); 00946 else if ( needEllipseMarker( element ) ) 00947 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "EllipseMarker", element ); 00948 else 00949 l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element ); 00950 00951 return l; 00952 } 00953 00954 bool QgsSymbolLayerV2Utils::hasExternalGraphic( QDomElement &element ) 00955 { 00956 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00957 if ( graphicElem.isNull() ) 00958 return false; 00959 00960 QDomElement externalGraphicElem = graphicElem.firstChildElement( "ExternalGraphic" ); 00961 if ( externalGraphicElem.isNull() ) 00962 return false; 00963 00964 // check for format 00965 QDomElement formatElem = externalGraphicElem.firstChildElement( "Format" ); 00966 if ( formatElem.isNull() ) 00967 return false; 00968 00969 QString format = formatElem.firstChild().nodeValue(); 00970 if ( format != "image/svg+xml" ) 00971 { 00972 QgsDebugMsg( "unsupported External Graphic format found: " + format ); 00973 return false; 00974 } 00975 00976 // check for a valid content 00977 QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( "OnlineResource" ); 00978 QDomElement inlineContentElem = externalGraphicElem.firstChildElement( "InlineContent" ); 00979 if ( !onlineResourceElem.isNull() ) 00980 { 00981 return true; 00982 } 00983 else if ( !inlineContentElem.isNull() ) 00984 { 00985 return false; // not implemented yet 00986 } 00987 else 00988 { 00989 return false; 00990 } 00991 00992 return false; 00993 } 00994 00995 bool QgsSymbolLayerV2Utils::hasWellKnownMark( QDomElement &element ) 00996 { 00997 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00998 if ( graphicElem.isNull() ) 00999 return false; 01000 01001 QDomElement markElem = graphicElem.firstChildElement( "Mark" ); 01002 if ( markElem.isNull() ) 01003 return false; 01004 01005 QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" ); 01006 if ( wellKnownNameElem.isNull() ) 01007 return false; 01008 01009 return true; 01010 } 01011 01012 01013 bool QgsSymbolLayerV2Utils::needFontMarker( QDomElement &element ) 01014 { 01015 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01016 if ( graphicElem.isNull() ) 01017 return false; 01018 01019 QDomElement markElem = graphicElem.firstChildElement( "Mark" ); 01020 if ( markElem.isNull() ) 01021 return false; 01022 01023 // check for format 01024 QDomElement formatElem = markElem.firstChildElement( "Format" ); 01025 if ( formatElem.isNull() ) 01026 return false; 01027 01028 QString format = formatElem.firstChild().nodeValue(); 01029 if ( format != "ttf" ) 01030 { 01031 QgsDebugMsg( "unsupported Graphic Mark format found: " + format ); 01032 return false; 01033 } 01034 01035 // check for a valid content 01036 QDomElement onlineResourceElem = markElem.firstChildElement( "OnlineResource" ); 01037 QDomElement inlineContentElem = markElem.firstChildElement( "InlineContent" ); 01038 if ( !onlineResourceElem.isNull() ) 01039 { 01040 // mark with ttf format has a markIndex element 01041 QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" ); 01042 if ( !markIndexElem.isNull() ) 01043 return true; 01044 } 01045 else if ( !inlineContentElem.isNull() ) 01046 { 01047 return false; // not implemented yet 01048 } 01049 01050 return false; 01051 } 01052 01053 bool QgsSymbolLayerV2Utils::needSvgMarker( QDomElement &element ) 01054 { 01055 return hasExternalGraphic( element ); 01056 } 01057 01058 bool QgsSymbolLayerV2Utils::needEllipseMarker( QDomElement &element ) 01059 { 01060 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01061 if ( graphicElem.isNull() ) 01062 return false; 01063 01064 QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem ); 01065 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it ) 01066 { 01067 if ( it.key() == "widthHeightFactor" ) 01068 { 01069 return true; 01070 } 01071 } 01072 01073 return false; 01074 } 01075 01076 bool QgsSymbolLayerV2Utils::needMarkerLine( QDomElement &element ) 01077 { 01078 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 01079 if ( strokeElem.isNull() ) 01080 return false; 01081 01082 QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" ); 01083 if ( graphicStrokeElem.isNull() ) 01084 return false; 01085 01086 return hasWellKnownMark( graphicStrokeElem ); 01087 } 01088 01089 bool QgsSymbolLayerV2Utils::needLinePatternFill( QDomElement &element ) { Q_UNUSED( element ); return false; } 01090 bool QgsSymbolLayerV2Utils::needPointPatternFill( QDomElement &element ) { Q_UNUSED( element ); return false; } 01091 01092 bool QgsSymbolLayerV2Utils::needSvgFill( QDomElement &element ) 01093 { 01094 QDomElement fillElem = element.firstChildElement( "Fill" ); 01095 if ( fillElem.isNull() ) 01096 return false; 01097 01098 QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" ); 01099 if ( graphicFillElem.isNull() ) 01100 return false; 01101 01102 return hasExternalGraphic( graphicFillElem ); 01103 } 01104 01105 01106 bool QgsSymbolLayerV2Utils::convertPolygonSymbolizerToPointMarker( QDomElement &element, QgsSymbolLayerV2List &layerList ) 01107 { 01108 QgsDebugMsg( "Entered." ); 01109 01110 /* SE 1.1 says about PolygonSymbolizer: 01111 if a point geometry is referenced instead of a polygon, 01112 then a small, square, ortho-normal polygon should be 01113 constructed for rendering. 01114 */ 01115 01116 QgsSymbolLayerV2List layers; 01117 01118 // retrieve both Fill and Stroke elements 01119 QDomElement fillElem = element.firstChildElement( "Fill" ); 01120 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 01121 01122 // first symbol layer 01123 { 01124 bool validFill = false, validBorder = false; 01125 01126 // check for simple fill 01127 // Fill element can contain some SvgParameter elements 01128 QColor fillColor; 01129 Qt::BrushStyle fillStyle; 01130 01131 if ( fillFromSld( fillElem, fillStyle, fillColor ) ) 01132 validFill = true; 01133 01134 // check for simple outline 01135 // Stroke element can contain some SvgParameter elements 01136 QColor borderColor; 01137 Qt::PenStyle borderStyle; 01138 double borderWidth = 1.0, dashOffset = 0.0; 01139 QVector<qreal> customDashPattern; 01140 01141 if ( lineFromSld( strokeElem, borderStyle, borderColor, borderWidth, 01142 0, 0, &customDashPattern, &dashOffset ) ) 01143 validBorder = true; 01144 01145 if ( validFill || validBorder ) 01146 { 01147 QgsStringMap map; 01148 map["name"] = "square"; 01149 map["color"] = QgsSymbolLayerV2Utils::encodeColor( validFill ? fillColor : Qt::transparent ); 01150 map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( validBorder ? borderColor : Qt::transparent ); 01151 map["size"] = QString::number( 6 ); 01152 map["angle"] = QString::number( 0 ); 01153 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( QPointF( 0, 0 ) ); 01154 layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SimpleMarker", map ) ); 01155 } 01156 } 01157 01158 // second symbol layer 01159 { 01160 bool validFill = false, validBorder = false; 01161 01162 // check for graphic fill 01163 QString name, format; 01164 int markIndex = -1; 01165 QColor fillColor, borderColor; 01166 double borderWidth = 1, size, angle = 0.0; 01167 QPointF anchor, offset; 01168 01169 // Fill element can contain a GraphicFill element 01170 QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" ); 01171 if ( !graphicFillElem.isNull() ) 01172 { 01173 // GraphicFill element must contain a Graphic element 01174 QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" ); 01175 if ( !graphicElem.isNull() ) 01176 { 01177 // Graphic element can contains some ExternalGraphic and Mark element 01178 // search for the first supported one and use it 01179 bool found = false; 01180 01181 QDomElement graphicChildElem = graphicElem.firstChildElement(); 01182 while ( !graphicChildElem.isNull() ) 01183 { 01184 if ( graphicChildElem.localName() == "Mark" ) 01185 { 01186 // check for a well known name 01187 QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( "WellKnownName" ); 01188 if ( !wellKnownNameElem.isNull() ) 01189 { 01190 name = wellKnownNameElem.firstChild().nodeValue(); 01191 found = true; 01192 break; 01193 } 01194 } 01195 01196 if ( graphicChildElem.localName() == "ExternalGraphic" || graphicChildElem.localName() == "Mark" ) 01197 { 01198 // check for external graphic format 01199 QDomElement formatElem = graphicChildElem.firstChildElement( "Format" ); 01200 if ( formatElem.isNull() ) 01201 continue; 01202 01203 format = formatElem.firstChild().nodeValue(); 01204 01205 // TODO: remove this check when more formats will be supported 01206 // only SVG external graphics are supported in this moment 01207 if ( graphicChildElem.localName() == "ExternalGraphic" && format != "image/svg+xml" ) 01208 continue; 01209 01210 // TODO: remove this check when more formats will be supported 01211 // only ttf marks are supported in this moment 01212 if ( graphicChildElem.localName() == "Mark" && format != "ttf" ) 01213 continue; 01214 01215 // check for a valid content 01216 QDomElement onlineResourceElem = graphicChildElem.firstChildElement( "OnlineResource" ); 01217 QDomElement inlineContentElem = graphicChildElem.firstChildElement( "InlineContent" ); 01218 01219 if ( !onlineResourceElem.isNull() ) 01220 { 01221 name = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" ); 01222 01223 if ( graphicChildElem.localName() == "Mark" && format == "ttf" ) 01224 { 01225 // mark with ttf format may have a name like ttf://fontFamily 01226 if ( name.startsWith( "ttf://" ) ) 01227 name = name.mid( 6 ); 01228 01229 // mark with ttf format has a markIndex element 01230 QDomElement markIndexElem = graphicChildElem.firstChildElement( "MarkIndex" ); 01231 if ( markIndexElem.isNull() ) 01232 continue; 01233 01234 bool ok; 01235 int v = markIndexElem.firstChild().nodeValue().toInt( &ok ); 01236 if ( !ok || v < 0 ) 01237 continue; 01238 01239 markIndex = v; 01240 } 01241 01242 found = true; 01243 break; 01244 } 01245 else if ( !inlineContentElem.isNull() ) 01246 continue; // TODO: not implemeneted yet 01247 else 01248 continue; 01249 } 01250 01251 // if Mark element is present but it doesn't contains neither 01252 // WellKnownName nor OnlineResource nor InlineContent, 01253 // use the default mark (square) 01254 if ( graphicChildElem.localName() == "Mark" ) 01255 { 01256 name = "square"; 01257 found = true; 01258 break; 01259 } 01260 } 01261 01262 // if found a valid Mark, check for its Fill and Stroke element 01263 if ( found && graphicChildElem.localName() == "Mark" ) 01264 { 01265 // XXX: recursive definition!?! couldn't be dangerous??? 01266 // to avoid recursion we handle only simple fill and simple stroke 01267 01268 // check for simple fill 01269 // Fill element can contain some SvgParameter elements 01270 Qt::BrushStyle markFillStyle; 01271 01272 QDomElement markFillElem = graphicChildElem.firstChildElement( "Fill" ); 01273 if ( fillFromSld( markFillElem, markFillStyle, fillColor ) ) 01274 validFill = true; 01275 01276 // check for simple outline 01277 // Stroke element can contain some SvgParameter elements 01278 Qt::PenStyle borderStyle; 01279 double borderWidth = 1.0, dashOffset = 0.0; 01280 QVector<qreal> customDashPattern; 01281 01282 QDomElement markStrokeElem = graphicChildElem.firstChildElement( "Stroke" ); 01283 if ( lineFromSld( markStrokeElem, borderStyle, borderColor, borderWidth, 01284 0, 0, &customDashPattern, &dashOffset ) ) 01285 validBorder = true; 01286 } 01287 01288 if ( found ) 01289 { 01290 // check for Opacity, Size, Rotation, AnchorPoint, Displacement 01291 QDomElement opacityElem = graphicElem.firstChildElement( "Opacity" ); 01292 if ( !opacityElem.isNull() ) 01293 fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) ); 01294 01295 QDomElement sizeElem = graphicElem.firstChildElement( "Size" ); 01296 if ( !sizeElem.isNull() ) 01297 { 01298 bool ok; 01299 double v = sizeElem.firstChild().nodeValue().toDouble( &ok ); 01300 if ( ok && v > 0 ) 01301 size = v; 01302 } 01303 01304 QString angleFunc; 01305 if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() ) 01306 { 01307 bool ok; 01308 double v = angleFunc.toDouble( &ok ); 01309 if ( ok ) 01310 angle = v; 01311 } 01312 01313 displacementFromSldElement( graphicElem, offset ); 01314 } 01315 } 01316 } 01317 01318 if ( validFill || validBorder ) 01319 { 01320 if ( format == "image/svg+xml" ) 01321 { 01322 QgsStringMap map; 01323 map["name"] = name; 01324 map["fill"] = fillColor.name(); 01325 map["outline"] = borderColor.name(); 01326 map["outline-width"] = QString::number( borderWidth ); 01327 if ( size > 0 ) 01328 map["size"] = QString::number( size ); 01329 if ( !doubleNear( angle, 0.0 ) ) 01330 map["angle"] = QString::number( angle ); 01331 if ( !offset.isNull() ) 01332 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( offset ); 01333 layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SvgMarker", map ) ); 01334 } 01335 else if ( format == "ttf" ) 01336 { 01337 QgsStringMap map; 01338 map["font"] = name; 01339 map["chr"] = markIndex; 01340 map["color"] = QgsSymbolLayerV2Utils::encodeColor( validFill ? fillColor : Qt::transparent ); 01341 if ( size > 0 ) 01342 map["size"] = QString::number( size ); 01343 if ( !doubleNear( angle, 0.0 ) ) 01344 map["angle"] = QString::number( angle ); 01345 if ( !offset.isNull() ) 01346 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( offset ); 01347 layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "FontMarker", map ) ); 01348 } 01349 } 01350 } 01351 01352 if ( layers.isEmpty() ) 01353 return false; 01354 01355 layerList << layers; 01356 layers.clear(); 01357 return true; 01358 } 01359 01360 void QgsSymbolLayerV2Utils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, QColor color ) 01361 { 01362 QString patternName; 01363 switch ( brushStyle ) 01364 { 01365 case Qt::NoBrush: 01366 return; 01367 01368 case Qt::SolidPattern: 01369 if ( color.isValid() ) 01370 { 01371 element.appendChild( createSvgParameterElement( doc, "fill", color.name() ) ); 01372 if ( color.alpha() < 255 ) 01373 element.appendChild( createSvgParameterElement( doc, "fill-opacity", encodeSldAlpha( color.alpha() ) ) ); 01374 } 01375 return; 01376 01377 case Qt::CrossPattern: 01378 case Qt::DiagCrossPattern: 01379 case Qt::HorPattern: 01380 case Qt::VerPattern: 01381 case Qt::BDiagPattern: 01382 case Qt::FDiagPattern: 01383 case Qt::Dense1Pattern: 01384 case Qt::Dense2Pattern: 01385 case Qt::Dense3Pattern: 01386 case Qt::Dense4Pattern: 01387 case Qt::Dense5Pattern: 01388 case Qt::Dense6Pattern: 01389 case Qt::Dense7Pattern: 01390 patternName = encodeSldBrushStyle( brushStyle ); 01391 break; 01392 01393 default: 01394 element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) ); 01395 return; 01396 } 01397 01398 QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" ); 01399 element.appendChild( graphicFillElem ); 01400 01401 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 01402 graphicFillElem.appendChild( graphicElem ); 01403 01404 QColor fillColor = patternName.startsWith( "brush://" ) ? color : QColor(); 01405 QColor borderColor = !patternName.startsWith( "brush://" ) ? color : QColor(); 01406 01407 /* Use WellKnownName tag to handle QT brush styles. */ 01408 wellKnownMarkerToSld( doc, graphicFillElem, patternName, fillColor, borderColor ); 01409 } 01410 01411 bool QgsSymbolLayerV2Utils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color ) 01412 { 01413 QgsDebugMsg( "Entered." ); 01414 01415 brushStyle = Qt::SolidPattern; 01416 color = QColor( "#808080" ); 01417 01418 if ( element.isNull() ) 01419 { 01420 brushStyle = Qt::NoBrush; 01421 color = QColor(); 01422 return true; 01423 } 01424 01425 QDomElement graphicFillElem = element.firstChildElement( "GraphicFill" ); 01426 // if no GraphicFill element is found, it's a solid fill 01427 if ( graphicFillElem.isNull() ) 01428 { 01429 QgsStringMap svgParams = getSvgParameterList( element ); 01430 for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it ) 01431 { 01432 QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) ); 01433 01434 if ( it.key() == "fill" ) 01435 color = QColor( it.value() ); 01436 else if ( it.key() == "fill-opacity" ) 01437 color.setAlpha( decodeSldAlpha( it.value() ) ); 01438 } 01439 } 01440 else // wellKnown marker 01441 { 01442 QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" ); 01443 if ( graphicElem.isNull() ) 01444 return false; // Graphic is required within GraphicFill 01445 01446 QString patternName = "square"; 01447 QColor fillColor, borderColor; 01448 double borderWidth, size; 01449 if ( !wellKnownMarkerFromSld( graphicFillElem, patternName, fillColor, borderColor, borderWidth, size ) ) 01450 return false; 01451 01452 brushStyle = decodeSldBrushStyle( patternName ); 01453 if ( brushStyle == Qt::NoBrush ) 01454 return false; // unable to decode brush style 01455 01456 QColor c = patternName.startsWith( "brush://" ) ? fillColor : borderColor; 01457 if ( c.isValid() ) 01458 color = c; 01459 } 01460 01461 return true; 01462 } 01463 01464 void QgsSymbolLayerV2Utils::lineToSld( QDomDocument &doc, QDomElement &element, 01465 Qt::PenStyle penStyle, QColor color, double width, 01466 const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle, 01467 const QVector<qreal> *customDashPattern, double dashOffset ) 01468 { 01469 QVector<qreal> dashPattern; 01470 if ( penStyle == Qt::CustomDashLine && !customDashPattern ) 01471 { 01472 element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) ); 01473 penStyle = Qt::DashLine; 01474 customDashPattern = &dashPattern; 01475 } 01476 01477 switch ( penStyle ) 01478 { 01479 case Qt::NoPen: 01480 return; 01481 01482 case Qt::SolidLine: 01483 break; 01484 01485 case Qt::DashLine: 01486 dashPattern.push_back( 4.0 ); 01487 dashPattern.push_back( 2.0 ); 01488 break; 01489 case Qt::DotLine: 01490 dashPattern.push_back( 1.0 ); 01491 dashPattern.push_back( 2.0 ); 01492 break; 01493 case Qt::DashDotLine: 01494 dashPattern.push_back( 4.0 ); 01495 dashPattern.push_back( 2.0 ); 01496 dashPattern.push_back( 1.0 ); 01497 dashPattern.push_back( 2.0 ); 01498 break; 01499 case Qt::DashDotDotLine: 01500 dashPattern.push_back( 4.0 ); 01501 dashPattern.push_back( 2.0 ); 01502 dashPattern.push_back( 1.0 ); 01503 dashPattern.push_back( 2.0 ); 01504 dashPattern.push_back( 1.0 ); 01505 dashPattern.push_back( 2.0 ); 01506 break; 01507 01508 case Qt::CustomDashLine: 01509 Q_ASSERT( customDashPattern ); 01510 break; 01511 01512 default: 01513 element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) ); 01514 return; 01515 } 01516 01517 if ( color.isValid() ) 01518 { 01519 element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) ); 01520 if ( color.alpha() < 255 ) 01521 element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) ); 01522 } 01523 if ( width > 0 ) 01524 element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) ); 01525 if ( penJoinStyle ) 01526 element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) ); 01527 if ( penCapStyle ) 01528 element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) ); 01529 01530 if ( customDashPattern && customDashPattern->size() > 0 ) 01531 { 01532 element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *customDashPattern ) ) ); 01533 if ( !doubleNear( dashOffset, 0.0 ) ) 01534 element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) ); 01535 } 01536 } 01537 01538 01539 bool QgsSymbolLayerV2Utils::lineFromSld( QDomElement &element, 01540 Qt::PenStyle &penStyle, QColor &color, double &width, 01541 Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle, 01542 QVector<qreal> *customDashPattern, double *dashOffset ) 01543 { 01544 QgsDebugMsg( "Entered." ); 01545 01546 penStyle = Qt::SolidLine; 01547 color = QColor( "#000000" ); 01548 width = 1; 01549 if ( penJoinStyle ) 01550 *penJoinStyle = Qt::BevelJoin; 01551 if ( penCapStyle ) 01552 *penCapStyle = Qt::SquareCap; 01553 if ( customDashPattern ) 01554 customDashPattern->clear(); 01555 if ( dashOffset ) 01556 *dashOffset = 0; 01557 01558 if ( element.isNull() ) 01559 { 01560 penStyle = Qt::NoPen; 01561 color = QColor(); 01562 return true; 01563 } 01564 01565 QgsStringMap svgParams = getSvgParameterList( element ); 01566 for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it ) 01567 { 01568 QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) ); 01569 01570 if ( it.key() == "stroke" ) 01571 { 01572 color = QColor( it.value() ); 01573 } 01574 else if ( it.key() == "stroke-opacity" ) 01575 { 01576 color.setAlpha( decodeSldAlpha( it.value() ) ); 01577 } 01578 else if ( it.key() == "stroke-width" ) 01579 { 01580 bool ok; 01581 double w = it.value().toDouble( &ok ); 01582 if ( ok ) 01583 width = w; 01584 } 01585 else if ( it.key() == "stroke-linejoin" && penJoinStyle ) 01586 { 01587 *penJoinStyle = decodeSldLineJoinStyle( it.value() ); 01588 } 01589 else if ( it.key() == "stroke-linecap" && penCapStyle ) 01590 { 01591 *penCapStyle = decodeSldLineCapStyle( it.value() ); 01592 } 01593 else if ( it.key() == "stroke-dasharray" && customDashPattern ) 01594 { 01595 *customDashPattern = decodeSldRealVector( it.value() ); 01596 if ( customDashPattern->size() > 0 ) 01597 { 01598 // convert the dasharray to one of the QT pen style, 01599 // if no match is found then set pen style to CustomDashLine 01600 bool dashPatternFound = false; 01601 01602 if ( customDashPattern->count() == 2 ) 01603 { 01604 if ( customDashPattern->at( 0 ) == 4.0 && 01605 customDashPattern->at( 1 ) == 2.0 ) 01606 { 01607 penStyle = Qt::DashLine; 01608 dashPatternFound = true; 01609 } 01610 else if ( customDashPattern->at( 0 ) == 1.0 && 01611 customDashPattern->at( 1 ) == 2.0 ) 01612 { 01613 penStyle = Qt::DotLine; 01614 dashPatternFound = true; 01615 } 01616 } 01617 else if ( customDashPattern->count() == 4 ) 01618 { 01619 if ( customDashPattern->at( 0 ) == 4.0 && 01620 customDashPattern->at( 1 ) == 2.0 && 01621 customDashPattern->at( 2 ) == 1.0 && 01622 customDashPattern->at( 3 ) == 2.0 ) 01623 { 01624 penStyle = Qt::DashDotLine; 01625 dashPatternFound = true; 01626 } 01627 } 01628 else if ( customDashPattern->count() == 6 ) 01629 { 01630 if ( customDashPattern->at( 0 ) == 4.0 && 01631 customDashPattern->at( 1 ) == 2.0 && 01632 customDashPattern->at( 2 ) == 1.0 && 01633 customDashPattern->at( 3 ) == 2.0 && 01634 customDashPattern->at( 4 ) == 1.0 && 01635 customDashPattern->at( 5 ) == 2.0 ) 01636 { 01637 penStyle = Qt::DashDotDotLine; 01638 dashPatternFound = true; 01639 } 01640 } 01641 01642 // default case: set pen style to CustomDashLine 01643 if ( !dashPatternFound ) 01644 { 01645 penStyle = Qt::CustomDashLine; 01646 } 01647 } 01648 } 01649 else if ( it.key() == "stroke-dashoffset" && dashOffset ) 01650 { 01651 bool ok; 01652 double d = it.value().toDouble( &ok ); 01653 if ( ok ) 01654 *dashOffset = d; 01655 } 01656 } 01657 01658 return true; 01659 } 01660 01661 void QgsSymbolLayerV2Utils::externalGraphicToSld( QDomDocument &doc, QDomElement &element, 01662 QString path, QString mime, 01663 QColor color, double size ) 01664 { 01665 QDomElement externalGraphicElem = doc.createElement( "se:ExternalGraphic" ); 01666 element.appendChild( externalGraphicElem ); 01667 01668 createOnlineResourceElement( doc, externalGraphicElem, path, mime ); 01669 01670 //TODO: missing a way to handle svg color. Should use <se:ColorReplacement> 01671 Q_UNUSED( color ); 01672 01673 if ( size >= 0 ) 01674 { 01675 QDomElement sizeElem = doc.createElement( "se:Size" ); 01676 sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) ); 01677 element.appendChild( sizeElem ); 01678 } 01679 } 01680 01681 bool QgsSymbolLayerV2Utils::externalGraphicFromSld( QDomElement &element, 01682 QString &path, QString &mime, 01683 QColor &color, double &size ) 01684 { 01685 QgsDebugMsg( "Entered." ); 01686 Q_UNUSED( color ); 01687 01688 QDomElement externalGraphicElem = element.firstChildElement( "ExternalGraphic" ); 01689 if ( externalGraphicElem.isNull() ) 01690 return false; 01691 01692 onlineResourceFromSldElement( externalGraphicElem, path, mime ); 01693 01694 QDomElement sizeElem = element.firstChildElement( "Size" ); 01695 if ( !sizeElem.isNull() ) 01696 { 01697 bool ok; 01698 double s = sizeElem.firstChild().nodeValue().toDouble( &ok ); 01699 if ( ok ) 01700 size = s; 01701 } 01702 01703 return true; 01704 } 01705 01706 void QgsSymbolLayerV2Utils::externalMarkerToSld( QDomDocument &doc, QDomElement &element, 01707 QString path, QString format, int *markIndex, 01708 QColor color, double size ) 01709 { 01710 QDomElement markElem = doc.createElement( "se:Mark" ); 01711 element.appendChild( markElem ); 01712 01713 createOnlineResourceElement( doc, markElem, path, format ); 01714 01715 if ( markIndex ) 01716 { 01717 QDomElement markIndexElem = doc.createElement( "se:MarkIndex" ); 01718 markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) ); 01719 markElem.appendChild( markIndexElem ); 01720 } 01721 01722 // <Fill> 01723 QDomElement fillElem = doc.createElement( "se:Fill" ); 01724 fillToSld( doc, fillElem, Qt::SolidPattern, color ); 01725 markElem.appendChild( fillElem ); 01726 01727 // <Size> 01728 if ( !doubleNear( size, 0.0 ) && size > 0 ) 01729 { 01730 QDomElement sizeElem = doc.createElement( "se:Size" ); 01731 sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) ); 01732 element.appendChild( sizeElem ); 01733 } 01734 } 01735 01736 bool QgsSymbolLayerV2Utils::externalMarkerFromSld( QDomElement &element, 01737 QString &path, QString &format, int &markIndex, 01738 QColor &color, double &size ) 01739 { 01740 QgsDebugMsg( "Entered." ); 01741 01742 color = QColor(); 01743 markIndex = -1; 01744 size = -1; 01745 01746 QDomElement markElem = element.firstChildElement( "Mark" ); 01747 if ( markElem.isNull() ) 01748 return false; 01749 01750 onlineResourceFromSldElement( markElem, path, format ); 01751 01752 QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" ); 01753 if ( !markIndexElem.isNull() ) 01754 { 01755 bool ok; 01756 int i = markIndexElem.firstChild().nodeValue().toInt( &ok ); 01757 if ( ok ) 01758 markIndex = i; 01759 } 01760 01761 // <Fill> 01762 QDomElement fillElem = markElem.firstChildElement( "Fill" ); 01763 Qt::BrushStyle b = Qt::SolidPattern; 01764 fillFromSld( fillElem, b, color ); 01765 // ignore brush style, solid expected 01766 01767 // <Size> 01768 QDomElement sizeElem = element.firstChildElement( "Size" ); 01769 if ( !sizeElem.isNull() ) 01770 { 01771 bool ok; 01772 double s = sizeElem.firstChild().nodeValue().toDouble( &ok ); 01773 if ( ok ) 01774 size = s; 01775 } 01776 01777 return true; 01778 } 01779 01780 void QgsSymbolLayerV2Utils::wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element, 01781 QString name, QColor color, QColor borderColor, 01782 double borderWidth, double size ) 01783 { 01784 QDomElement markElem = doc.createElement( "se:Mark" ); 01785 element.appendChild( markElem ); 01786 01787 QDomElement wellKnownNameElem = doc.createElement( "se:WellKnownName" ); 01788 wellKnownNameElem.appendChild( doc.createTextNode( name ) ); 01789 markElem.appendChild( wellKnownNameElem ); 01790 01791 // <Fill> 01792 if ( color.isValid() ) 01793 { 01794 QDomElement fillElem = doc.createElement( "se:Fill" ); 01795 fillToSld( doc, fillElem, Qt::SolidPattern, color ); 01796 markElem.appendChild( fillElem ); 01797 } 01798 01799 // <Stroke> 01800 if ( borderColor.isValid() ) 01801 { 01802 QDomElement strokeElem = doc.createElement( "se:Stroke" ); 01803 lineToSld( doc, strokeElem, Qt::SolidLine, borderColor, borderWidth ); 01804 markElem.appendChild( strokeElem ); 01805 } 01806 01807 // <Size> 01808 if ( !doubleNear( size, 0.0 ) && size > 0 ) 01809 { 01810 QDomElement sizeElem = doc.createElement( "se:Size" ); 01811 sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) ); 01812 element.appendChild( sizeElem ); 01813 } 01814 } 01815 01816 bool QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( QDomElement &element, 01817 QString &name, QColor &color, QColor &borderColor, 01818 double &borderWidth, double &size ) 01819 { 01820 QgsDebugMsg( "Entered." ); 01821 01822 name = "square"; 01823 color = QColor(); 01824 borderColor = QColor( "#000000" ); 01825 borderWidth = 1; 01826 size = 6; 01827 01828 QDomElement markElem = element.firstChildElement( "Mark" ); 01829 if ( markElem.isNull() ) 01830 return false; 01831 01832 QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" ); 01833 if ( !wellKnownNameElem.isNull() ) 01834 { 01835 name = wellKnownNameElem.firstChild().nodeValue(); 01836 QgsDebugMsg( "found Mark with well known name: " + name ); 01837 } 01838 01839 // <Fill> 01840 QDomElement fillElem = markElem.firstChildElement( "Fill" ); 01841 Qt::BrushStyle b = Qt::SolidPattern; 01842 fillFromSld( fillElem, b, color ); 01843 // ignore brush style, solid expected 01844 01845 // <Stroke> 01846 QDomElement strokeElem = markElem.firstChildElement( "Stroke" ); 01847 Qt::PenStyle p = Qt::SolidLine; 01848 lineFromSld( strokeElem, p, borderColor, borderWidth ); 01849 // ignore border style, solid expected 01850 01851 // <Size> 01852 QDomElement sizeElem = element.firstChildElement( "Size" ); 01853 if ( !sizeElem.isNull() ) 01854 { 01855 bool ok; 01856 double s = sizeElem.firstChild().nodeValue().toDouble( &ok ); 01857 if ( ok ) 01858 size = s; 01859 } 01860 01861 return true; 01862 } 01863 01864 void QgsSymbolLayerV2Utils::createRotationElement( QDomDocument &doc, QDomElement &element, QString rotationFunc ) 01865 { 01866 if ( !rotationFunc.isEmpty() ) 01867 { 01868 QDomElement rotationElem = doc.createElement( "se:Rotation" ); 01869 createFunctionElement( doc, rotationElem, rotationFunc ); 01870 element.appendChild( rotationElem ); 01871 } 01872 } 01873 01874 bool QgsSymbolLayerV2Utils::rotationFromSldElement( QDomElement &element, QString &rotationFunc ) 01875 { 01876 QDomElement rotationElem = element.firstChildElement( "Rotation" ); 01877 if ( !rotationElem.isNull() ) 01878 { 01879 functionFromSldElement( rotationElem, rotationFunc ); 01880 } 01881 return true; 01882 } 01883 01884 01885 void QgsSymbolLayerV2Utils::createOpacityElement( QDomDocument &doc, QDomElement &element, QString alphaFunc ) 01886 { 01887 if ( !alphaFunc.isEmpty() ) 01888 { 01889 QDomElement opacityElem = doc.createElement( "se:Opacity" ); 01890 createFunctionElement( doc, opacityElem, alphaFunc ); 01891 element.appendChild( opacityElem ); 01892 } 01893 } 01894 01895 bool QgsSymbolLayerV2Utils::opacityFromSldElement( QDomElement &element, QString &alphaFunc ) 01896 { 01897 QDomElement opacityElem = element.firstChildElement( "Opacity" ); 01898 if ( !opacityElem.isNull() ) 01899 { 01900 functionFromSldElement( opacityElem, alphaFunc ); 01901 } 01902 return true; 01903 } 01904 01905 void QgsSymbolLayerV2Utils::createDisplacementElement( QDomDocument &doc, QDomElement &element, QPointF offset ) 01906 { 01907 if ( offset.isNull() ) 01908 return; 01909 01910 QDomElement displacementElem = doc.createElement( "se:Displacement" ); 01911 element.appendChild( displacementElem ); 01912 01913 QDomElement dispXElem = doc.createElement( "se:DisplacementX" ); 01914 dispXElem.appendChild( doc.createTextNode( QString::number( offset.x() ) ) ); 01915 01916 QDomElement dispYElem = doc.createElement( "se:DisplacementY" ); 01917 dispYElem.appendChild( doc.createTextNode( QString::number( offset.y() ) ) ); 01918 01919 displacementElem.appendChild( dispXElem ); 01920 displacementElem.appendChild( dispYElem ); 01921 } 01922 01923 bool QgsSymbolLayerV2Utils::displacementFromSldElement( QDomElement &element, QPointF &offset ) 01924 { 01925 offset = QPointF( 0, 0 ); 01926 01927 QDomElement displacementElem = element.firstChildElement( "Displacement" ); 01928 if ( displacementElem.isNull() ) 01929 return true; 01930 01931 QDomElement dispXElem = element.firstChildElement( "DisplacementX" ); 01932 if ( !dispXElem.isNull() ) 01933 { 01934 bool ok; 01935 double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok ); 01936 if ( ok ) 01937 offset.setX( offsetX ); 01938 } 01939 01940 QDomElement dispYElem = element.firstChildElement( "DisplacementY" ); 01941 if ( !dispYElem.isNull() ) 01942 { 01943 bool ok; 01944 double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok ); 01945 if ( ok ) 01946 offset.setY( offsetY ); 01947 } 01948 01949 return true; 01950 } 01951 01952 void QgsSymbolLayerV2Utils::labelTextToSld( QDomDocument &doc, QDomElement &element, 01953 QString label, QFont font, 01954 QColor color, double size ) 01955 { 01956 QDomElement labelElem = doc.createElement( "se:Label" ); 01957 labelElem.appendChild( doc.createTextNode( label ) ); 01958 element.appendChild( labelElem ); 01959 01960 QDomElement fontElem = doc.createElement( "se:Font" ); 01961 element.appendChild( fontElem ); 01962 01963 fontElem.appendChild( createSvgParameterElement( doc, "font-family", font.family() ) ); 01964 #if 0 01965 fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) ); 01966 fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) ); 01967 #endif 01968 fontElem.appendChild( createSvgParameterElement( doc, "font-size", QString::number( size ) ) ); 01969 01970 // <Fill> 01971 if ( color.isValid() ) 01972 { 01973 QDomElement fillElem = doc.createElement( "Fill" ); 01974 fillToSld( doc, fillElem, Qt::SolidPattern, color ); 01975 element.appendChild( fillElem ); 01976 } 01977 } 01978 01979 void QgsSymbolLayerV2Utils::createGeometryElement( QDomDocument &doc, QDomElement &element, QString geomFunc ) 01980 { 01981 if ( geomFunc.isEmpty() ) 01982 return; 01983 01984 QDomElement geometryElem = doc.createElement( "Geometry" ); 01985 element.appendChild( geometryElem ); 01986 01987 /* About using a function withing the Geometry tag. 01988 * 01989 * The SLD specification <= 1.1 is vague: 01990 * "In principle, a fixed geometry could be defined using GML or 01991 * operators could be defined for computing the geometry from 01992 * references or literals. However, using a feature property directly 01993 * is by far the most commonly useful method." 01994 * 01995 * Even if it seems that specs should take care all the possible cases, 01996 * looking at the XML schema fragment that encodes the Geometry element, 01997 * it has to be a PropertyName element: 01998 * <xsd:element name="Geometry"> 01999 * <xsd:complexType> 02000 * <xsd:sequence> 02001 * <xsd:element ref="ogc:PropertyName"/> 02002 * </xsd:sequence> 02003 * </xsd:complexType> 02004 * </xsd:element> 02005 * 02006 * Anyway we will use a ogc:Function to handle geometry transformations 02007 * like offset, centroid, ... 02008 */ 02009 02010 createFunctionElement( doc, geometryElem, geomFunc ); 02011 } 02012 02013 bool QgsSymbolLayerV2Utils::geometryFromSldElement( QDomElement &element, QString &geomFunc ) 02014 { 02015 QDomElement geometryElem = element.firstChildElement( "Geometry" ); 02016 if ( geometryElem.isNull() ) 02017 return true; 02018 02019 return functionFromSldElement( geometryElem, geomFunc ); 02020 } 02021 02022 bool QgsSymbolLayerV2Utils::createFunctionElement( QDomDocument &doc, QDomElement &element, QString function ) 02023 { 02024 // let's use QgsExpression to generate the SLD for the function 02025 QgsExpression expr( function ); 02026 if ( expr.hasParserError() ) 02027 { 02028 element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) ); 02029 return false; 02030 } 02031 expr.toOgcFilter( doc, element ); 02032 return true; 02033 } 02034 02035 bool QgsSymbolLayerV2Utils::functionFromSldElement( QDomElement &element, QString &function ) 02036 { 02037 QgsDebugMsg( "Entered." ); 02038 02039 QgsExpression *expr = QgsExpression::createFromOgcFilter( element ); 02040 if ( !expr ) 02041 return false; 02042 02043 bool valid = expr->hasParserError(); 02044 if ( !valid ) 02045 { 02046 QgsDebugMsg( "parser error: " + expr->parserErrorString() ); 02047 } 02048 else 02049 { 02050 function = expr->dump(); 02051 } 02052 02053 delete expr; 02054 return valid; 02055 } 02056 02057 void QgsSymbolLayerV2Utils::createOnlineResourceElement( QDomDocument &doc, QDomElement &element, 02058 QString path, QString format ) 02059 { 02060 QDomElement onlineResourceElem = doc.createElement( "OnlineResource" ); 02061 onlineResourceElem.setAttribute( "xlink:type", "simple" ); 02062 onlineResourceElem.setAttribute( "xlink:href", path ); 02063 element.appendChild( onlineResourceElem ); 02064 02065 QDomElement formatElem = doc.createElement( "Format" ); 02066 formatElem.appendChild( doc.createTextNode( format ) ); 02067 element.appendChild( formatElem ); 02068 } 02069 02070 bool QgsSymbolLayerV2Utils::onlineResourceFromSldElement( QDomElement &element, QString &path, QString &format ) 02071 { 02072 QgsDebugMsg( "Entered." ); 02073 02074 QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" ); 02075 if ( onlineResourceElem.isNull() ) 02076 return false; 02077 02078 path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" ); 02079 02080 QDomElement formatElem = element.firstChildElement( "Format" ); 02081 if ( formatElem.isNull() ) 02082 return false; // OnlineResource requires a Format sibling element 02083 02084 format = formatElem.firstChild().nodeValue(); 02085 return true; 02086 } 02087 02088 02089 QDomElement QgsSymbolLayerV2Utils::createSvgParameterElement( QDomDocument &doc, QString name, QString value ) 02090 { 02091 QDomElement nodeElem = doc.createElement( "se:SvgParameter" ); 02092 nodeElem.setAttribute( "name", name ); 02093 nodeElem.appendChild( doc.createTextNode( value ) ); 02094 return nodeElem; 02095 } 02096 02097 QgsStringMap QgsSymbolLayerV2Utils::getSvgParameterList( QDomElement &element ) 02098 { 02099 QgsStringMap params; 02100 02101 QDomElement paramElem = element.firstChildElement(); 02102 while ( !paramElem.isNull() ) 02103 { 02104 if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" ) 02105 { 02106 QString name = paramElem.attribute( "name" ); 02107 QString value = paramElem.firstChild().nodeValue(); 02108 02109 if ( !name.isEmpty() && !value.isEmpty() ) 02110 params[ name ] = value; 02111 } 02112 02113 paramElem = paramElem.nextSiblingElement(); 02114 } 02115 02116 return params; 02117 } 02118 02119 QDomElement QgsSymbolLayerV2Utils::createVendorOptionElement( QDomDocument &doc, QString name, QString value ) 02120 { 02121 QDomElement nodeElem = doc.createElement( "VendorOption" ); 02122 nodeElem.setAttribute( "name", name ); 02123 nodeElem.appendChild( doc.createTextNode( value ) ); 02124 return nodeElem; 02125 } 02126 02127 QgsStringMap QgsSymbolLayerV2Utils::getVendorOptionList( QDomElement &element ) 02128 { 02129 QgsStringMap params; 02130 02131 QDomElement paramElem = element.firstChildElement( "VendorOption" ); 02132 while ( !paramElem.isNull() ) 02133 { 02134 QString name = paramElem.attribute( "name" ); 02135 QString value = paramElem.firstChild().nodeValue(); 02136 02137 if ( !name.isEmpty() && !value.isEmpty() ) 02138 params[ name ] = value; 02139 02140 paramElem = paramElem.nextSiblingElement( "VendorOption" ); 02141 } 02142 02143 return params; 02144 } 02145 02146 02147 QgsStringMap QgsSymbolLayerV2Utils::parseProperties( QDomElement& element ) 02148 { 02149 QgsStringMap props; 02150 QDomElement e = element.firstChildElement(); 02151 while ( !e.isNull() ) 02152 { 02153 if ( e.tagName() != "prop" ) 02154 { 02155 QgsDebugMsg( "unknown tag " + e.tagName() ); 02156 } 02157 else 02158 { 02159 QString propKey = e.attribute( "k" ); 02160 QString propValue = e.attribute( "v" ); 02161 props[propKey] = propValue; 02162 } 02163 e = e.nextSiblingElement(); 02164 } 02165 return props; 02166 } 02167 02168 02169 void QgsSymbolLayerV2Utils::saveProperties( QgsStringMap props, QDomDocument& doc, QDomElement& element ) 02170 { 02171 for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it ) 02172 { 02173 QDomElement propEl = doc.createElement( "prop" ); 02174 propEl.setAttribute( "k", it.key() ); 02175 propEl.setAttribute( "v", it.value() ); 02176 element.appendChild( propEl ); 02177 } 02178 } 02179 02180 QgsSymbolV2Map QgsSymbolLayerV2Utils::loadSymbols( QDomElement& element ) 02181 { 02182 // go through symbols one-by-one and load them 02183 02184 QgsSymbolV2Map symbols; 02185 QDomElement e = element.firstChildElement(); 02186 02187 while ( !e.isNull() ) 02188 { 02189 if ( e.tagName() == "symbol" ) 02190 { 02191 QgsSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol( e ); 02192 if ( symbol != NULL ) 02193 symbols.insert( e.attribute( "name" ), symbol ); 02194 } 02195 else 02196 { 02197 QgsDebugMsg( "unknown tag: " + e.tagName() ); 02198 } 02199 e = e.nextSiblingElement(); 02200 } 02201 02202 02203 // now walk through the list of symbols and find those prefixed with @ 02204 // these symbols are sub-symbols of some other symbol layers 02205 // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo" 02206 QStringList subsymbols; 02207 02208 for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it ) 02209 { 02210 if ( it.key()[0] != '@' ) 02211 continue; 02212 02213 // add to array (for deletion) 02214 subsymbols.append( it.key() ); 02215 02216 QStringList parts = it.key().split( "@" ); 02217 if ( parts.count() < 3 ) 02218 { 02219 QgsDebugMsg( "found subsymbol with invalid name: " + it.key() ); 02220 delete it.value(); // we must delete it 02221 continue; // some invalid syntax 02222 } 02223 QString symname = parts[1]; 02224 int symlayer = parts[2].toInt(); 02225 02226 if ( !symbols.contains( symname ) ) 02227 { 02228 QgsDebugMsg( "subsymbol references invalid symbol: " + symname ); 02229 delete it.value(); // we must delete it 02230 continue; 02231 } 02232 02233 QgsSymbolV2* sym = symbols[symname]; 02234 if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() ) 02235 { 02236 QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) ); 02237 delete it.value(); // we must delete it 02238 continue; 02239 } 02240 02241 // set subsymbol takes ownership 02242 bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() ); 02243 if ( !res ) 02244 { 02245 QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() ); 02246 } 02247 02248 02249 } 02250 02251 // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away) 02252 for ( int i = 0; i < subsymbols.count(); i++ ) 02253 symbols.take( subsymbols[i] ); 02254 02255 return symbols; 02256 } 02257 02258 QDomElement QgsSymbolLayerV2Utils::saveSymbols( QgsSymbolV2Map& symbols, QString tagName, QDomDocument& doc ) 02259 { 02260 QDomElement symbolsElem = doc.createElement( tagName ); 02261 02262 QMap<QString, QgsSymbolV2*> subSymbols; 02263 02264 // save symbols 02265 for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its ) 02266 { 02267 QDomElement symEl = saveSymbol( its.key(), its.value(), doc, &subSymbols ); 02268 symbolsElem.appendChild( symEl ); 02269 } 02270 02271 // add subsymbols, don't allow subsymbols for them (to keep things simple) 02272 for ( QMap<QString, QgsSymbolV2*>::iterator itsub = subSymbols.begin(); itsub != subSymbols.end(); ++itsub ) 02273 { 02274 QDomElement subsymEl = saveSymbol( itsub.key(), itsub.value(), doc ); 02275 symbolsElem.appendChild( subsymEl ); 02276 } 02277 02278 return symbolsElem; 02279 } 02280 02281 void QgsSymbolLayerV2Utils::clearSymbolMap( QgsSymbolV2Map& symbols ) 02282 { 02283 foreach( QString name, symbols.keys() ) 02284 { 02285 delete symbols.value( name ); 02286 } 02287 symbols.clear(); 02288 } 02289 02290 02291 QgsVectorColorRampV2* QgsSymbolLayerV2Utils::loadColorRamp( QDomElement& element ) 02292 { 02293 QString rampType = element.attribute( "type" ); 02294 02295 // parse properties 02296 QgsStringMap props = QgsSymbolLayerV2Utils::parseProperties( element ); 02297 02298 if ( rampType == "gradient" ) 02299 return QgsVectorGradientColorRampV2::create( props ); 02300 else if ( rampType == "random" ) 02301 return QgsVectorRandomColorRampV2::create( props ); 02302 else if ( rampType == "colorbrewer" ) 02303 return QgsVectorColorBrewerColorRampV2::create( props ); 02304 else 02305 { 02306 QgsDebugMsg( "unknown colorramp type " + rampType ); 02307 return NULL; 02308 } 02309 } 02310 02311 02312 QDomElement QgsSymbolLayerV2Utils::saveColorRamp( QString name, QgsVectorColorRampV2* ramp, QDomDocument& doc ) 02313 { 02314 QDomElement rampEl = doc.createElement( "colorramp" ); 02315 rampEl.setAttribute( "type", ramp->type() ); 02316 rampEl.setAttribute( "name", name ); 02317 02318 QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl ); 02319 return rampEl; 02320 } 02321 02322 double QgsSymbolLayerV2Utils::lineWidthScaleFactor( QgsRenderContext& c, QgsSymbolV2::OutputUnit u ) 02323 { 02324 02325 if ( u == QgsSymbolV2::MM ) 02326 { 02327 return c.scaleFactor(); 02328 } 02329 else //QgsSymbol::MapUnit 02330 { 02331 double mup = c.mapToPixel().mapUnitsPerPixel(); 02332 if ( mup > 0 ) 02333 { 02334 return 1.0 / mup; 02335 } 02336 else 02337 { 02338 return 1.0; 02339 } 02340 } 02341 } 02342 02343 double QgsSymbolLayerV2Utils::pixelSizeScaleFactor( QgsRenderContext& c, QgsSymbolV2::OutputUnit u ) 02344 { 02345 if ( u == QgsSymbolV2::MM ) 02346 { 02347 return ( c.scaleFactor() * c.rasterScaleFactor() ); 02348 } 02349 else //QgsSymbol::MapUnit 02350 { 02351 double mup = c.mapToPixel().mapUnitsPerPixel(); 02352 if ( mup > 0 ) 02353 { 02354 return c.rasterScaleFactor() / c.mapToPixel().mapUnitsPerPixel(); 02355 } 02356 else 02357 { 02358 return 1.0; 02359 } 02360 } 02361 } 02362 02363 QgsRenderContext QgsSymbolLayerV2Utils::createRenderContext( QPainter* p ) 02364 { 02365 QgsRenderContext context; 02366 context.setPainter( p ); 02367 context.setRasterScaleFactor( 1.0 ); 02368 if ( p && p->device() ) 02369 { 02370 context.setScaleFactor( p->device()->logicalDpiX() / 25.4 ); 02371 } 02372 else 02373 { 02374 context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value 02375 } 02376 return context; 02377 } 02378 02379 void QgsSymbolLayerV2Utils::multiplyImageOpacity( QImage* image, qreal alpha ) 02380 { 02381 if ( !image ) 02382 { 02383 return; 02384 } 02385 02386 QRgb myRgb; 02387 QImage::Format format = image->format(); 02388 if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 ) 02389 { 02390 QgsDebugMsg( "no alpha channel." ); 02391 return; 02392 } 02393 02394 //change the alpha component of every pixel 02395 for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex ) 02396 { 02397 QRgb* scanLine = ( QRgb* )image->scanLine( heightIndex ); 02398 for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex ) 02399 { 02400 myRgb = scanLine[widthIndex]; 02401 if ( format == QImage::Format_ARGB32_Premultiplied ) 02402 scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) ); 02403 else 02404 scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) ); 02405 } 02406 } 02407 } 02408 02409 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs ) 02410 { 02411 switch ( lhs.type() ) 02412 { 02413 case QVariant::Int: 02414 return lhs.toInt() < rhs.toInt(); 02415 case QVariant::UInt: 02416 return lhs.toUInt() < rhs.toUInt(); 02417 case QVariant::LongLong: 02418 return lhs.toLongLong() < rhs.toLongLong(); 02419 case QVariant::ULongLong: 02420 return lhs.toULongLong() < rhs.toULongLong(); 02421 case QVariant::Double: 02422 return lhs.toDouble() < rhs.toDouble(); 02423 case QVariant::Char: 02424 return lhs.toChar() < rhs.toChar(); 02425 case QVariant::Date: 02426 return lhs.toDate() < rhs.toDate(); 02427 case QVariant::Time: 02428 return lhs.toTime() < rhs.toTime(); 02429 case QVariant::DateTime: 02430 return lhs.toDateTime() < rhs.toDateTime(); 02431 default: 02432 return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0; 02433 } 02434 } 02435 02436 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs ) 02437 { 02438 return ! _QVariantLessThan( lhs, rhs ); 02439 } 02440 02441 void QgsSymbolLayerV2Utils::sortVariantList( QList<QVariant>& list, Qt::SortOrder order ) 02442 { 02443 if ( order == Qt::AscendingOrder ) 02444 { 02445 qSort( list.begin(), list.end(), _QVariantLessThan ); 02446 } 02447 else // Qt::DescendingOrder 02448 { 02449 qSort( list.begin(), list.end(), _QVariantGreaterThan ); 02450 } 02451 } 02452 02453 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) 02454 { 02455 double dx = directionPoint.x() - startPoint.x(); 02456 double dy = directionPoint.y() - startPoint.y(); 02457 double length = sqrt( dx * dx + dy * dy ); 02458 double scaleFactor = distance / length; 02459 return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor ); 02460 }