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