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