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