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