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