QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgssymbollayerv2utils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssymbollayerv2utils.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgssymbollayerv2utils.h"
17 
18 #include "qgssymbollayerv2.h"
20 #include "qgssymbolv2.h"
21 #include "qgsvectorcolorrampv2.h"
22 #include "qgsexpression.h"
23 #include "qgsapplication.h"
24 #include "qgsproject.h"
25 #include "qgsogcutils.h"
26 
27 #include "qgsapplication.h"
28 #include "qgsproject.h"
29 #include "qgslogger.h"
30 #include "qgsrendercontext.h"
31 
32 #include <QColor>
33 #include <QFont>
34 #include <QDomDocument>
35 #include <QDomNode>
36 #include <QDomElement>
37 #include <QIcon>
38 #include <QPainter>
39 #include <QSettings>
40 
41 QString QgsSymbolLayerV2Utils::encodeColor( QColor color )
42 {
43  return QString( "%1,%2,%3,%4" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ).arg( color.alpha() );
44 }
45 
47 {
48  QStringList lst = str.split( "," );
49  if ( lst.count() < 3 )
50  {
51  return QColor( str );
52  }
53  int red, green, blue, alpha;
54  red = lst[0].toInt();
55  green = lst[1].toInt();
56  blue = lst[2].toInt();
57  alpha = 255;
58  if ( lst.count() > 3 )
59  {
60  alpha = lst[3].toInt();
61  }
62  return QColor( red, green, blue, alpha );
63 }
64 
66 {
67  return QString::number( alpha / 255.0, 'f', 2 );
68 }
69 
71 {
72  bool ok;
73  double alpha = str.toDouble( &ok );
74  if ( !ok || alpha > 1 )
75  alpha = 255;
76  else if ( alpha < 0 )
77  alpha = 0;
78  return alpha * 255;
79 }
80 
81 QString QgsSymbolLayerV2Utils::encodeSldFontStyle( QFont::Style style )
82 {
83  switch ( style )
84  {
85  case QFont::StyleNormal: return "normal";
86  case QFont::StyleItalic: return "italic";
87  case QFont::StyleOblique: return "oblique";
88  default: return "";
89  }
90 }
91 
92 QFont::Style QgsSymbolLayerV2Utils::decodeSldFontStyle( QString str )
93 {
94  if ( str == "normal" ) return QFont::StyleNormal;
95  if ( str == "italic" ) return QFont::StyleItalic;
96  if ( str == "oblique" ) return QFont::StyleOblique;
97  return QFont::StyleNormal;
98 }
99 
101 {
102  if ( weight == 50 ) return "normal";
103  if ( weight == 75 ) return "bold";
104 
105  // QFont::Weight is between 0 and 99
106  // CSS font-weight is between 100 and 900
107  if ( weight < 0 ) return "100";
108  if ( weight > 99 ) return "900";
109  return QString::number( weight * 800 / 99 + 100 );
110 }
111 
113 {
114  bool ok;
115  int weight = str.toInt( &ok );
116  if ( !ok ) return ( int ) QFont::Normal;
117 
118  // CSS font-weight is between 100 and 900
119  // QFont::Weight is between 0 and 99
120  if ( weight > 900 ) return 99;
121  if ( weight < 100 ) return 0;
122  return ( weight - 100 ) * 99 / 800;
123 }
124 
125 QString QgsSymbolLayerV2Utils::encodePenStyle( Qt::PenStyle style )
126 {
127  switch ( style )
128  {
129  case Qt::NoPen: return "no";
130  case Qt::SolidLine: return "solid";
131  case Qt::DashLine: return "dash";
132  case Qt::DotLine: return "dot";
133  case Qt::DashDotLine: return "dash dot";
134  case Qt::DashDotDotLine: return "dash dot dot";
135  default: return "???";
136  }
137 }
138 
139 Qt::PenStyle QgsSymbolLayerV2Utils::decodePenStyle( QString str )
140 {
141  if ( str == "no" ) return Qt::NoPen;
142  if ( str == "solid" ) return Qt::SolidLine;
143  if ( str == "dash" ) return Qt::DashLine;
144  if ( str == "dot" ) return Qt::DotLine;
145  if ( str == "dash dot" ) return Qt::DashDotLine;
146  if ( str == "dash dot dot" ) return Qt::DashDotDotLine;
147  return Qt::SolidLine;
148 }
149 
150 QString QgsSymbolLayerV2Utils::encodePenJoinStyle( Qt::PenJoinStyle style )
151 {
152  switch ( style )
153  {
154  case Qt::BevelJoin: return "bevel";
155  case Qt::MiterJoin: return "miter";
156  case Qt::RoundJoin: return "round";
157  default: return "???";
158  }
159 }
160 
161 Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodePenJoinStyle( QString str )
162 {
163  if ( str == "bevel" ) return Qt::BevelJoin;
164  if ( str == "miter" ) return Qt::MiterJoin;
165  if ( str == "round" ) return Qt::RoundJoin;
166  return Qt::BevelJoin;
167 }
168 
169 QString QgsSymbolLayerV2Utils::encodeSldLineJoinStyle( Qt::PenJoinStyle style )
170 {
171  switch ( style )
172  {
173  case Qt::BevelJoin: return "bevel";
174  case Qt::MiterJoin: return "mitre";
175  case Qt::RoundJoin: return "round";
176  default: return "";
177  }
178 }
179 
180 Qt::PenJoinStyle QgsSymbolLayerV2Utils::decodeSldLineJoinStyle( QString str )
181 {
182  if ( str == "bevel" ) return Qt::BevelJoin;
183  if ( str == "mitre" ) return Qt::MiterJoin;
184  if ( str == "round" ) return Qt::RoundJoin;
185  return Qt::BevelJoin;
186 }
187 
188 QString QgsSymbolLayerV2Utils::encodePenCapStyle( Qt::PenCapStyle style )
189 {
190  switch ( style )
191  {
192  case Qt::SquareCap: return "square";
193  case Qt::FlatCap: return "flat";
194  case Qt::RoundCap: return "round";
195  default: return "???";
196  }
197 }
198 
199 Qt::PenCapStyle QgsSymbolLayerV2Utils::decodePenCapStyle( QString str )
200 {
201  if ( str == "square" ) return Qt::SquareCap;
202  if ( str == "flat" ) return Qt::FlatCap;
203  if ( str == "round" ) return Qt::RoundCap;
204  return Qt::SquareCap;
205 }
206 
207 QString QgsSymbolLayerV2Utils::encodeSldLineCapStyle( Qt::PenCapStyle style )
208 {
209  switch ( style )
210  {
211  case Qt::SquareCap: return "square";
212  case Qt::FlatCap: return "butt";
213  case Qt::RoundCap: return "round";
214  default: return "";
215  }
216 }
217 
218 Qt::PenCapStyle QgsSymbolLayerV2Utils::decodeSldLineCapStyle( QString str )
219 {
220  if ( str == "square" ) return Qt::SquareCap;
221  if ( str == "butt" ) return Qt::FlatCap;
222  if ( str == "round" ) return Qt::RoundCap;
223  return Qt::SquareCap;
224 }
225 
226 QString QgsSymbolLayerV2Utils::encodeBrushStyle( Qt::BrushStyle style )
227 {
228  switch ( style )
229  {
230  case Qt::SolidPattern : return "solid";
231  case Qt::HorPattern : return "horizontal";
232  case Qt::VerPattern : return "vertical";
233  case Qt::CrossPattern : return "cross";
234  case Qt::BDiagPattern : return "b_diagonal";
235  case Qt::FDiagPattern : return "f_diagonal";
236  case Qt::DiagCrossPattern : return "diagonal_x";
237  case Qt::Dense1Pattern : return "dense1";
238  case Qt::Dense2Pattern : return "dense2";
239  case Qt::Dense3Pattern : return "dense3";
240  case Qt::Dense4Pattern : return "dense4";
241  case Qt::Dense5Pattern : return "dense5";
242  case Qt::Dense6Pattern : return "dense6";
243  case Qt::Dense7Pattern : return "dense7";
244  case Qt::NoBrush : return "no";
245  default: return "???";
246  }
247 }
248 
249 Qt::BrushStyle QgsSymbolLayerV2Utils::decodeBrushStyle( QString str )
250 {
251  if ( str == "solid" ) return Qt::SolidPattern;
252  if ( str == "horizontal" ) return Qt::HorPattern;
253  if ( str == "vertical" ) return Qt::VerPattern;
254  if ( str == "cross" ) return Qt::CrossPattern;
255  if ( str == "b_diagonal" ) return Qt::BDiagPattern;
256  if ( str == "f_diagonal" ) return Qt::FDiagPattern;
257  if ( str == "diagonal_x" ) return Qt::DiagCrossPattern;
258  if ( str == "dense1" ) return Qt::Dense1Pattern;
259  if ( str == "dense2" ) return Qt::Dense2Pattern;
260  if ( str == "dense3" ) return Qt::Dense3Pattern;
261  if ( str == "dense4" ) return Qt::Dense4Pattern;
262  if ( str == "dense5" ) return Qt::Dense5Pattern;
263  if ( str == "dense6" ) return Qt::Dense6Pattern;
264  if ( str == "dense7" ) return Qt::Dense7Pattern;
265  if ( str == "no" ) return Qt::NoBrush;
266  return Qt::SolidPattern;
267 }
268 
269 QString QgsSymbolLayerV2Utils::encodeSldBrushStyle( Qt::BrushStyle style )
270 {
271  switch ( style )
272  {
273  case Qt::CrossPattern: return "cross";
274  case Qt::DiagCrossPattern: return "x";
275 
276  /* The following names are taken from the presentation "GeoServer
277  * Cartographic Rendering" by Andrea Aime at the FOSS4G 2010.
278  * (see http://2010.foss4g.org/presentations/3588.pdf)
279  */
280  case Qt::HorPattern: return "horline";
281  case Qt::VerPattern: return "line";
282  case Qt::BDiagPattern: return "slash";
283  case Qt::FDiagPattern: return "backslash";
284 
285  /* define the other names following the same pattern used above */
286  case Qt::Dense1Pattern:
287  case Qt::Dense2Pattern:
288  case Qt::Dense3Pattern:
289  case Qt::Dense4Pattern:
290  case Qt::Dense5Pattern:
291  case Qt::Dense6Pattern:
292  case Qt::Dense7Pattern:
293  return QString( "brush://%1" ).arg( encodeBrushStyle( style ) );
294 
295  default:
296  return QString();
297  }
298 }
299 
300 Qt::BrushStyle QgsSymbolLayerV2Utils::decodeSldBrushStyle( QString str )
301 {
302  if ( str == "horline" ) return Qt::HorPattern;
303  if ( str == "line" ) return Qt::VerPattern;
304  if ( str == "cross" ) return Qt::CrossPattern;
305  if ( str == "slash" ) return Qt::BDiagPattern;
306  if ( str == "backshash" ) return Qt::FDiagPattern;
307  if ( str == "x" ) return Qt::DiagCrossPattern;
308 
309  if ( str.startsWith( "brush://" ) )
310  return decodeBrushStyle( str.mid( 8 ) );
311 
312  return Qt::NoBrush;
313 }
314 
315 QString QgsSymbolLayerV2Utils::encodePoint( QPointF point )
316 {
317  return QString( "%1,%2" ).arg( point.x() ).arg( point.y() );
318 }
319 
321 {
322  QStringList lst = str.split( ',' );
323  if ( lst.count() != 2 )
324  return QPointF( 0, 0 );
325  return QPointF( lst[0].toDouble(), lst[1].toDouble() );
326 }
327 
329 {
330  switch ( unit )
331  {
332  case QgsSymbolV2::MM:
333  return "MM";
335  return "MapUnit";
336  default:
337  return "MM";
338  }
339 }
340 
342 {
343  if ( str == "MM" )
344  {
345  return QgsSymbolV2::MM;
346  }
347  else if ( str == "MapUnit" )
348  {
349  return QgsSymbolV2::MapUnit;
350  }
351 
352  // milimeters are default
353  return QgsSymbolV2::MM;
354 }
355 
357 {
358  switch ( unit )
359  {
361  if ( scaleFactor )
362  *scaleFactor = 0.001; // from millimeters to meters
363  return "http://www.opengeospatial.org/se/units/metre";
364 
365  case QgsSymbolV2::MM:
366  default:
367  // pixel is the SLD default uom. The "standardized rendering pixel
368  // size" is defined to be 0.28mm × 0.28mm (millimeters).
369  if ( scaleFactor )
370  *scaleFactor = 0.28; // from millimeters to pixels
371 
372  // http://www.opengeospatial.org/sld/units/pixel
373  return QString();
374  }
375 }
376 
378 {
379  if ( str == "http://www.opengeospatial.org/se/units/metre" )
380  {
381  if ( scaleFactor )
382  *scaleFactor = 1000.0; // from meters to millimeters
383  return QgsSymbolV2::MapUnit;
384  }
385  else if ( str == "http://www.opengeospatial.org/se/units/foot" )
386  {
387  if ( scaleFactor )
388  *scaleFactor = 304.8; // from feet to meters
389  return QgsSymbolV2::MapUnit;
390  }
391 
392  // pixel is the SLD default uom. The "standardized rendering pixel
393  // size" is defined to be 0.28mm x 0.28mm (millimeters).
394  if ( scaleFactor )
395  *scaleFactor = 1 / 0.00028; // from pixels to millimeters
396  return QgsSymbolV2::MM;
397 }
398 
399 QString QgsSymbolLayerV2Utils::encodeRealVector( const QVector<qreal>& v )
400 {
401  QString vectorString;
402  QVector<qreal>::const_iterator it = v.constBegin();
403  for ( ; it != v.constEnd(); ++it )
404  {
405  if ( it != v.constBegin() )
406  {
407  vectorString.append( ";" );
408  }
409  vectorString.append( QString::number( *it ) );
410  }
411  return vectorString;
412 }
413 
414 QVector<qreal> QgsSymbolLayerV2Utils::decodeRealVector( const QString& s )
415 {
416  QVector<qreal> resultVector;
417 
418  QStringList realList = s.split( ";" );
419  QStringList::const_iterator it = realList.constBegin();
420  for ( ; it != realList.constEnd(); ++it )
421  {
422  resultVector.append( it->toDouble() );
423  }
424 
425  return resultVector;
426 }
427 
428 QString QgsSymbolLayerV2Utils::encodeSldRealVector( const QVector<qreal>& v )
429 {
430  QString vectorString;
431  QVector<qreal>::const_iterator it = v.constBegin();
432  for ( ; it != v.constEnd(); ++it )
433  {
434  if ( it != v.constBegin() )
435  {
436  vectorString.append( " " );
437  }
438  vectorString.append( QString::number( *it ) );
439  }
440  return vectorString;
441 }
442 
443 QVector<qreal> QgsSymbolLayerV2Utils::decodeSldRealVector( const QString& s )
444 {
445  QVector<qreal> resultVector;
446 
447  QStringList realList = s.split( " " );
448  QStringList::const_iterator it = realList.constBegin();
449  for ( ; it != realList.constEnd(); ++it )
450  {
451  resultVector.append( it->toDouble() );
452  }
453 
454  return resultVector;
455 }
456 
458 {
459  QString encodedValue;
460 
461  switch ( scaleMethod )
462  {
464  encodedValue = "diameter";
465  break;
467  encodedValue = "area";
468  break;
469  }
470  return encodedValue;
471 }
472 
474 {
475  QgsSymbolV2::ScaleMethod scaleMethod;
476 
477  if ( str == "diameter" )
478  {
479  scaleMethod = QgsSymbolV2::ScaleDiameter;
480  }
481  else
482  {
483  scaleMethod = QgsSymbolV2::ScaleArea;
484  }
485 
486  return scaleMethod;
487 }
488 
490 {
491  return QIcon( symbolPreviewPixmap( symbol, size ) );
492 }
493 
495 {
496  Q_ASSERT( symbol );
497 
498  QPixmap pixmap( size );
499  pixmap.fill( Qt::transparent );
500  QPainter painter;
501  painter.begin( &pixmap );
502  painter.setRenderHint( QPainter::Antialiasing );
503  symbol->drawPreviewIcon( &painter, size );
504  painter.end();
505  return pixmap;
506 }
507 
509 {
510  double maxBleed = 0;
511  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
512  {
513  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
514  double layerMaxBleed = layer->estimateMaxBleed();
515  maxBleed = layerMaxBleed > maxBleed ? layerMaxBleed : maxBleed;
516  }
517 
518  return maxBleed;
519 }
520 
522 {
523  QPixmap pixmap( size );
524  pixmap.fill( Qt::transparent );
525  QPainter painter;
526  painter.begin( &pixmap );
527  painter.setRenderHint( QPainter::Antialiasing );
528  QgsRenderContext renderContext = createRenderContext( &painter );
529  QgsSymbolV2RenderContext symbolContext( renderContext, u );
530  layer->drawPreviewIcon( symbolContext, size );
531  painter.end();
532  return QIcon( pixmap );
533 }
534 
536 {
537  return QIcon( colorRampPreviewPixmap( ramp, size ) );
538 }
539 
541 {
542  QPixmap pixmap( size );
543  pixmap.fill( Qt::transparent );
544  // pixmap.fill( Qt::white ); // this makes the background white instead of transparent
545  QPainter painter;
546  painter.begin( &pixmap );
547 
548  //draw stippled background, for transparent images
549  drawStippledBackround( &painter, QRect( 0, 0, size.width(), size.height() ) );
550 
551  // antialising makes the colors duller, and no point in antialiasing a color ramp
552  // painter.setRenderHint( QPainter::Antialiasing );
553  for ( int i = 0; i < size.width(); i++ )
554  {
555  QPen pen( ramp->color(( double ) i / size.width() ) );
556  painter.setPen( pen );
557  painter.drawLine( i, 0, i, size.height() - 1 );
558  }
559  painter.end();
560  return pixmap;
561 }
562 
563 void QgsSymbolLayerV2Utils::drawStippledBackround( QPainter* painter, QRect rect )
564 {
565  // create a 2x2 checker-board image
566  uchar pixDataRGB[] = { 255, 255, 255, 255,
567  127, 127, 127, 255,
568  127, 127, 127, 255,
569  255, 255, 255, 255
570  };
571  QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
572  // scale it to rect so at least 5 patterns are shown
573  int width = ( rect.width() < rect.height() ) ?
574  rect.width() / 2.5 : rect.height() / 2.5;
575  QPixmap pix = QPixmap::fromImage( img.scaled( width, width ) );
576  // fill rect with texture
577  QBrush brush;
578  brush.setTexture( pix );
579  painter->fillRect( rect, brush );
580 }
581 
582 #include <QPolygonF>
583 
584 #include <cmath>
585 #include <cfloat>
586 
587 
588 // calculate line's angle and tangent
589 static bool lineInfo( QPointF p1, QPointF p2, double& angle, double& t )
590 {
591  double x1 = p1.x(), y1 = p1.y(), x2 = p2.x(), y2 = p2.y();
592 
593  if ( x1 == x2 && y1 == y2 )
594  return false;
595 
596  // tangent
597  t = ( x1 == x2 ? DBL_MAX : ( y2 - y1 ) / ( x2 - x1 ) );
598 
599  // angle
600  if ( t == DBL_MAX )
601  angle = ( y2 > y1 ? M_PI / 2 : M_PI * 3 / 2 ); // angle is 90 or 270
602  else if ( t == 0 )
603  angle = ( x2 > x1 ? 0 : M_PI ); // angle is 0 or 180
604  else if ( t >= 0 )
605  angle = ( y2 > y1 ? atan( t ) : M_PI + atan( t ) );
606  else // t < 0
607  angle = ( y2 > y1 ? M_PI + atan( t ) : atan( t ) );
608 
609  return true;
610 }
611 
612 // offset a point with an angle and distance
613 static QPointF offsetPoint( QPointF pt, double angle, double dist )
614 {
615  return QPointF( pt.x() + dist * cos( angle ), pt.y() + dist * sin( angle ) );
616 }
617 
618 // calc intersection of two (infinite) lines defined by one point and tangent
619 static QPointF linesIntersection( QPointF p1, double t1, QPointF p2, double t2 )
620 {
621  // parallel lines? (or the difference between angles is less than appr. 10 degree)
622  if (( t1 == DBL_MAX && t2 == DBL_MAX ) || qAbs( atan( t1 ) - atan( t2 ) ) < 0.175 )
623  return QPointF();
624 
625  double x, y;
626  if ( t1 == DBL_MAX || t2 == DBL_MAX )
627  {
628  // in case one line is with angle 90 resp. 270 degrees (tangent undefined)
629  // swap them so that line 2 is with undefined tangent
630  if ( t1 == DBL_MAX )
631  {
632  QPointF pSwp = p1; p1 = p2; p2 = pSwp;
633  double tSwp = t1; t1 = t2; t2 = tSwp;
634  }
635 
636  x = p2.x();
637  }
638  else
639  {
640  // usual case
641  x = (( p1.y() - p2.y() ) + t2 * p2.x() - t1 * p1.x() ) / ( t2 - t1 );
642  }
643 
644  y = p1.y() + t1 * ( x - p1.x() );
645  return QPointF( x, y );
646 }
647 
648 
649 QPolygonF offsetLine( QPolygonF polyline, double dist )
650 {
651  QPolygonF newLine;
652 
653  if ( polyline.count() < 2 )
654  return newLine;
655 
656  double angle = 0.0, t_new, t_old = 0;
657  QPointF pt_old, pt_new;
658  QPointF p1 = polyline[0], p2;
659  bool first_point = true;
660 
661  for ( int i = 1; i < polyline.count(); i++ )
662  {
663  p2 = polyline[i];
664 
665  if ( !lineInfo( p1, p2, angle, t_new ) )
666  continue; // not a line...
667 
668  pt_new = offsetPoint( p1, angle + M_PI / 2, dist );
669 
670  if ( ! first_point )
671  {
672  // if it's not the first line segment
673  // calc intersection with last line (with offset)
674  QPointF pt_tmp = linesIntersection( pt_old, t_old, pt_new, t_new );
675  if ( !pt_tmp.isNull() )
676  pt_new = pt_tmp;
677  }
678 
679  newLine.append( pt_new );
680 
681  pt_old = pt_new;
682  t_old = t_new;
683  p1 = p2;
684  first_point = false;
685  }
686 
687  // last line segment:
688  pt_new = offsetPoint( p2, angle + M_PI / 2, dist );
689  newLine.append( pt_new );
690  return newLine;
691 }
692 
694 
695 
697 {
698  QgsSymbolLayerV2List layers;
699  QDomNode layerNode = element.firstChild();
700 
701  while ( !layerNode.isNull() )
702  {
703  QDomElement e = layerNode.toElement();
704  if ( !e.isNull() )
705  {
706  if ( e.tagName() != "layer" )
707  {
708  QgsDebugMsg( "unknown tag " + e.tagName() );
709  }
710  else
711  {
712  QgsSymbolLayerV2* layer = loadSymbolLayer( e );
713 
714  if ( layer != NULL )
715  {
716  // Dealing with sub-symbols nested into a layer
717  QDomElement s = e.firstChildElement( "symbol" );
718  if ( !s.isNull() )
719  {
720  QgsSymbolV2* subSymbol = loadSymbol( s );
721  bool res = layer->setSubSymbol( subSymbol );
722  if ( !res )
723  {
724  QgsDebugMsg( "symbol layer refused subsymbol: " + s.attribute( "name" ) );
725  }
726  }
727  layers.append( layer );
728  }
729  }
730  }
731  layerNode = layerNode.nextSibling();
732  }
733 
734  if ( layers.count() == 0 )
735  {
736  QgsDebugMsg( "no layers for symbol" );
737  return NULL;
738  }
739 
740  QString symbolType = element.attribute( "type" );
741 
742  QgsSymbolV2* symbol = 0;
743  if ( symbolType == "line" )
744  symbol = new QgsLineSymbolV2( layers );
745  else if ( symbolType == "fill" )
746  symbol = new QgsFillSymbolV2( layers );
747  else if ( symbolType == "marker" )
748  symbol = new QgsMarkerSymbolV2( layers );
749  else
750  {
751  QgsDebugMsg( "unknown symbol type " + symbolType );
752  return NULL;
753  }
754 
755  if ( element.hasAttribute( "outputUnit" ) )
756  {
757  symbol->setOutputUnit( decodeOutputUnit( element.attribute( "outputUnit" ) ) );
758  }
759  symbol->setAlpha( element.attribute( "alpha", "1.0" ).toDouble() );
760 
761  return symbol;
762 }
763 
765 {
766  QString layerClass = element.attribute( "class" );
767  bool locked = element.attribute( "locked" ).toInt();
768  int pass = element.attribute( "pass" ).toInt();
769 
770  // parse properties
771  QgsStringMap props = parseProperties( element );
772 
773  QgsSymbolLayerV2* layer;
774  layer = QgsSymbolLayerV2Registry::instance()->createSymbolLayer( layerClass, props );
775  if ( layer )
776  {
777  layer->setLocked( locked );
778  layer->setRenderingPass( pass );
779  return layer;
780  }
781  else
782  {
783  QgsDebugMsg( "unknown class " + layerClass );
784  return NULL;
785  }
786 }
787 
789 {
790  switch ( type )
791  {
792  case QgsSymbolV2::Line: return "line";
793  case QgsSymbolV2::Marker: return "marker";
794  case QgsSymbolV2::Fill: return "fill";
795  default: return "";
796  }
797 }
798 
799 QDomElement QgsSymbolLayerV2Utils::saveSymbol( QString name, QgsSymbolV2* symbol, QDomDocument& doc )
800 {
801  Q_ASSERT( symbol );
802  QDomElement symEl = doc.createElement( "symbol" );
803  symEl.setAttribute( "type", _nameForSymbolType( symbol->type() ) );
804  symEl.setAttribute( "name", name );
805  symEl.setAttribute( "alpha", QString::number( symbol->alpha() ) );
806  QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) );
807 
808  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
809  {
810  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
811 
812  QDomElement layerEl = doc.createElement( "layer" );
813  layerEl.setAttribute( "class", layer->layerType() );
814  layerEl.setAttribute( "locked", layer->isLocked() );
815  layerEl.setAttribute( "pass", layer->renderingPass() );
816  saveProperties( layer->properties(), doc, layerEl );
817  if ( layer->subSymbol() != NULL )
818  {
819  QString subname = QString( "@%1@%2" ).arg( name ).arg( i );
820  QDomElement subEl = saveSymbol( subname, layer->subSymbol(), doc );
821  layerEl.appendChild( subEl );
822  }
823  symEl.appendChild( layerEl );
824  }
825 
826  return symEl;
827 }
828 
829 
831  QGis::GeometryType geomType,
832  QgsSymbolLayerV2List &layers )
833 {
834  QgsDebugMsg( "Entered." );
835 
836  if ( element.isNull() )
837  return false;
838 
839  QgsSymbolLayerV2 *l = 0;
840 
841  QString symbolizerName = element.localName();
842 
843  if ( symbolizerName == "PointSymbolizer" )
844  {
845  // first check for Graphic element, nothing will be rendered if not found
846  QDomElement graphicElem = element.firstChildElement( "Graphic" );
847  if ( graphicElem.isNull() )
848  {
849  QgsDebugMsg( "Graphic element not found in PointSymbolizer" );
850  }
851  else
852  {
853  switch ( geomType )
854  {
855  case QGis::Polygon:
856  // polygon layer and point symbolizer: draw poligon centroid
857  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "CentroidFill", element );
858  if ( l )
859  layers.append( l );
860 
861  break;
862 
863  case QGis::Point:
864  // point layer and point symbolizer: use markers
865  l = createMarkerLayerFromSld( element );
866  if ( l )
867  layers.append( l );
868 
869  break;
870 
871  case QGis::Line:
872  // line layer and point symbolizer: draw central point
873  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
874  if ( l )
875  layers.append( l );
876 
877  break;
878 
879  default:
880  break;
881  }
882  }
883  }
884 
885  if ( symbolizerName == "LineSymbolizer" )
886  {
887  // check for Stroke element, nothing will be rendered if not found
888  QDomElement strokeElem = element.firstChildElement( "Stroke" );
889  if ( strokeElem.isNull() )
890  {
891  QgsDebugMsg( "Stroke element not found in LineSymbolizer" );
892  }
893  else
894  {
895  switch ( geomType )
896  {
897  case QGis::Polygon:
898  case QGis::Line:
899  // polygon layer and line symbolizer: draw polygon outline
900  // line layer and line symbolizer: draw line
901  l = createLineLayerFromSld( element );
902  if ( l )
903  layers.append( l );
904 
905  break;
906 
907  case QGis::Point:
908  // point layer and line symbolizer: draw a little line marker
909  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
910  if ( l )
911  layers.append( l );
912 
913  default:
914  break;
915  }
916  }
917  }
918 
919  if ( symbolizerName == "PolygonSymbolizer" )
920  {
921  // get Fill and Stroke elements, nothing will be rendered if both are missing
922  QDomElement fillElem = element.firstChildElement( "Fill" );
923  QDomElement strokeElem = element.firstChildElement( "Stroke" );
924  if ( fillElem.isNull() && strokeElem.isNull() )
925  {
926  QgsDebugMsg( "neither Fill nor Stroke element not found in PolygonSymbolizer" );
927  }
928  else
929  {
930  QgsSymbolLayerV2 *l = 0;
931 
932  switch ( geomType )
933  {
934  case QGis::Polygon:
935  // polygon layer and polygon symbolizer: draw fill
936 
937  l = createFillLayerFromSld( element );
938  if ( l )
939  {
940  layers.append( l );
941 
942  // SVGFill and SimpleFill symbolLayerV2 supports outline internally,
943  // so don't go forward to create a different symbolLayerV2 for outline
944  if ( l->layerType() == "SimpleFill" || l->layerType() == "SVGFill" )
945  break;
946  }
947 
948  // now create polygon outline
949  // polygon layer and polygon symbolizer: draw polygon outline
950  l = createLineLayerFromSld( element );
951  if ( l )
952  layers.append( l );
953 
954  break;
955 
956  case QGis::Line:
957  // line layer and polygon symbolizer: draw line
958  l = createLineLayerFromSld( element );
959  if ( l )
960  layers.append( l );
961 
962  break;
963 
964  case QGis::Point:
965  // point layer and polygon symbolizer: draw a square marker
966  convertPolygonSymbolizerToPointMarker( element, layers );
967  break;
968 
969  default:
970  break;
971  }
972  }
973  }
974 
975  return true;
976 }
977 
979 {
980  QDomElement fillElem = element.firstChildElement( "Fill" );
981  if ( fillElem.isNull() )
982  {
983  QgsDebugMsg( "Fill element not found" );
984  return NULL;
985  }
986 
987  QgsSymbolLayerV2 *l = 0;
988 
989  if ( needLinePatternFill( element ) )
990  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "LinePatternFill", element );
991  else if ( needPointPatternFill( element ) )
992  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "PointPatternFill", element );
993  else if ( needSvgFill( element ) )
995  else
996  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleFill", element );
997 
998  return l;
999 }
1000 
1002 {
1003  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1004  if ( strokeElem.isNull() )
1005  {
1006  QgsDebugMsg( "Stroke element not found" );
1007  return NULL;
1008  }
1009 
1010  QgsSymbolLayerV2 *l = 0;
1011 
1012  if ( needMarkerLine( element ) )
1013  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
1014  else
1015  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleLine", element );
1016 
1017  return l;
1018 }
1019 
1021 {
1022  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1023  if ( graphicElem.isNull() )
1024  {
1025  QgsDebugMsg( "Graphic element not found" );
1026  return NULL;
1027  }
1028 
1029  QgsSymbolLayerV2 *l = 0;
1030 
1031  if ( needFontMarker( element ) )
1032  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "FontMarker", element );
1033  else if ( needSvgMarker( element ) )
1034  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SvgMarker", element );
1035  else if ( needEllipseMarker( element ) )
1036  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "EllipseMarker", element );
1037  else
1038  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
1039 
1040  return l;
1041 }
1042 
1043 bool QgsSymbolLayerV2Utils::hasExternalGraphic( QDomElement &element )
1044 {
1045  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1046  if ( graphicElem.isNull() )
1047  return false;
1048 
1049  QDomElement externalGraphicElem = graphicElem.firstChildElement( "ExternalGraphic" );
1050  if ( externalGraphicElem.isNull() )
1051  return false;
1052 
1053  // check for format
1054  QDomElement formatElem = externalGraphicElem.firstChildElement( "Format" );
1055  if ( formatElem.isNull() )
1056  return false;
1057 
1058  QString format = formatElem.firstChild().nodeValue();
1059  if ( format != "image/svg+xml" )
1060  {
1061  QgsDebugMsg( "unsupported External Graphic format found: " + format );
1062  return false;
1063  }
1064 
1065  // check for a valid content
1066  QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( "OnlineResource" );
1067  QDomElement inlineContentElem = externalGraphicElem.firstChildElement( "InlineContent" );
1068  if ( !onlineResourceElem.isNull() )
1069  {
1070  return true;
1071  }
1072 #if 0
1073  else if ( !inlineContentElem.isNull() )
1074  {
1075  return false; // not implemented yet
1076  }
1077 #endif
1078  else
1079  {
1080  return false;
1081  }
1082 }
1083 
1084 bool QgsSymbolLayerV2Utils::hasWellKnownMark( QDomElement &element )
1085 {
1086  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1087  if ( graphicElem.isNull() )
1088  return false;
1089 
1090  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1091  if ( markElem.isNull() )
1092  return false;
1093 
1094  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
1095  if ( wellKnownNameElem.isNull() )
1096  return false;
1097 
1098  return true;
1099 }
1100 
1101 
1102 bool QgsSymbolLayerV2Utils::needFontMarker( QDomElement &element )
1103 {
1104  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1105  if ( graphicElem.isNull() )
1106  return false;
1107 
1108  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1109  if ( markElem.isNull() )
1110  return false;
1111 
1112  // check for format
1113  QDomElement formatElem = markElem.firstChildElement( "Format" );
1114  if ( formatElem.isNull() )
1115  return false;
1116 
1117  QString format = formatElem.firstChild().nodeValue();
1118  if ( format != "ttf" )
1119  {
1120  QgsDebugMsg( "unsupported Graphic Mark format found: " + format );
1121  return false;
1122  }
1123 
1124  // check for a valid content
1125  QDomElement onlineResourceElem = markElem.firstChildElement( "OnlineResource" );
1126  QDomElement inlineContentElem = markElem.firstChildElement( "InlineContent" );
1127  if ( !onlineResourceElem.isNull() )
1128  {
1129  // mark with ttf format has a markIndex element
1130  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
1131  if ( !markIndexElem.isNull() )
1132  return true;
1133  }
1134  else if ( !inlineContentElem.isNull() )
1135  {
1136  return false; // not implemented yet
1137  }
1138 
1139  return false;
1140 }
1141 
1142 bool QgsSymbolLayerV2Utils::needSvgMarker( QDomElement &element )
1143 {
1144  return hasExternalGraphic( element );
1145 }
1146 
1147 bool QgsSymbolLayerV2Utils::needEllipseMarker( QDomElement &element )
1148 {
1149  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1150  if ( graphicElem.isNull() )
1151  return false;
1152 
1153  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
1154  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1155  {
1156  if ( it.key() == "widthHeightFactor" )
1157  {
1158  return true;
1159  }
1160  }
1161 
1162  return false;
1163 }
1164 
1165 bool QgsSymbolLayerV2Utils::needMarkerLine( QDomElement &element )
1166 {
1167  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1168  if ( strokeElem.isNull() )
1169  return false;
1170 
1171  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1172  if ( graphicStrokeElem.isNull() )
1173  return false;
1174 
1175  return hasWellKnownMark( graphicStrokeElem );
1176 }
1177 
1179 {
1180  QDomElement fillElem = element.firstChildElement( "Fill" );
1181  if ( fillElem.isNull() )
1182  return false;
1183 
1184  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1185  if ( graphicFillElem.isNull() )
1186  return false;
1187 
1188  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1189  if ( graphicElem.isNull() )
1190  return false;
1191 
1192  // line pattern fill uses horline wellknown marker with an angle
1193 
1194  QString name;
1195  QColor fillColor, borderColor;
1196  double size, borderWidth;
1197  Qt::PenStyle borderStyle;
1198  if ( !wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderStyle, borderWidth, size ) )
1199  return false;
1200 
1201  if ( name != "horline" )
1202  return false;
1203 
1204  QString angleFunc;
1205  if ( !rotationFromSldElement( graphicElem, angleFunc ) )
1206  return false;
1207 
1208  bool ok;
1209  double angle = angleFunc.toDouble( &ok );
1210  if ( !ok || angle == 0 )
1211  return false;
1212 
1213  return true;
1214 }
1215 
1217 {
1218  Q_UNUSED( element );
1219  return false;
1220 }
1221 
1222 bool QgsSymbolLayerV2Utils::needSvgFill( QDomElement &element )
1223 {
1224  QDomElement fillElem = element.firstChildElement( "Fill" );
1225  if ( fillElem.isNull() )
1226  return false;
1227 
1228  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1229  if ( graphicFillElem.isNull() )
1230  return false;
1231 
1232  return hasExternalGraphic( graphicFillElem );
1233 }
1234 
1235 
1237 {
1238  QgsDebugMsg( "Entered." );
1239 
1240  /* SE 1.1 says about PolygonSymbolizer:
1241  if a point geometry is referenced instead of a polygon,
1242  then a small, square, ortho-normal polygon should be
1243  constructed for rendering.
1244  */
1245 
1246  QgsSymbolLayerV2List layers;
1247 
1248  // retrieve both Fill and Stroke elements
1249  QDomElement fillElem = element.firstChildElement( "Fill" );
1250  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1251 
1252  // first symbol layer
1253  {
1254  bool validFill = false, validBorder = false;
1255 
1256  // check for simple fill
1257  // Fill element can contain some SvgParameter elements
1258  QColor fillColor;
1259  Qt::BrushStyle fillStyle;
1260 
1261  if ( fillFromSld( fillElem, fillStyle, fillColor ) )
1262  validFill = true;
1263 
1264  // check for simple outline
1265  // Stroke element can contain some SvgParameter elements
1266  QColor borderColor;
1267  Qt::PenStyle borderStyle;
1268  double borderWidth = 1.0, dashOffset = 0.0;
1269  QVector<qreal> customDashPattern;
1270 
1271  if ( lineFromSld( strokeElem, borderStyle, borderColor, borderWidth,
1272  0, 0, &customDashPattern, &dashOffset ) )
1273  validBorder = true;
1274 
1275  if ( validFill || validBorder )
1276  {
1277  QgsStringMap map;
1278  map["name"] = "square";
1279  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1280  map["color_border"] = encodeColor( validBorder ? borderColor : Qt::transparent );
1281  map["size"] = QString::number( 6 );
1282  map["angle"] = QString::number( 0 );
1283  map["offset"] = encodePoint( QPointF( 0, 0 ) );
1284  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SimpleMarker", map ) );
1285  }
1286  }
1287 
1288  // second symbol layer
1289  {
1290  bool validFill = false, validBorder = false;
1291 
1292  // check for graphic fill
1293  QString name, format;
1294  int markIndex = -1;
1295  QColor fillColor, borderColor;
1296  double borderWidth = 1.0, size = 0.0, angle = 0.0;
1297  QPointF anchor, offset;
1298 
1299  // Fill element can contain a GraphicFill element
1300  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1301  if ( !graphicFillElem.isNull() )
1302  {
1303  // GraphicFill element must contain a Graphic element
1304  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1305  if ( !graphicElem.isNull() )
1306  {
1307  // Graphic element can contains some ExternalGraphic and Mark element
1308  // search for the first supported one and use it
1309  bool found = false;
1310 
1311  QDomElement graphicChildElem = graphicElem.firstChildElement();
1312  while ( !graphicChildElem.isNull() )
1313  {
1314  if ( graphicChildElem.localName() == "Mark" )
1315  {
1316  // check for a well known name
1317  QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( "WellKnownName" );
1318  if ( !wellKnownNameElem.isNull() )
1319  {
1320  name = wellKnownNameElem.firstChild().nodeValue();
1321  found = true;
1322  break;
1323  }
1324  }
1325 
1326  if ( graphicChildElem.localName() == "ExternalGraphic" || graphicChildElem.localName() == "Mark" )
1327  {
1328  // check for external graphic format
1329  QDomElement formatElem = graphicChildElem.firstChildElement( "Format" );
1330  if ( formatElem.isNull() )
1331  continue;
1332 
1333  format = formatElem.firstChild().nodeValue();
1334 
1335  // TODO: remove this check when more formats will be supported
1336  // only SVG external graphics are supported in this moment
1337  if ( graphicChildElem.localName() == "ExternalGraphic" && format != "image/svg+xml" )
1338  continue;
1339 
1340  // TODO: remove this check when more formats will be supported
1341  // only ttf marks are supported in this moment
1342  if ( graphicChildElem.localName() == "Mark" && format != "ttf" )
1343  continue;
1344 
1345  // check for a valid content
1346  QDomElement onlineResourceElem = graphicChildElem.firstChildElement( "OnlineResource" );
1347  QDomElement inlineContentElem = graphicChildElem.firstChildElement( "InlineContent" );
1348 
1349  if ( !onlineResourceElem.isNull() )
1350  {
1351  name = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
1352 
1353  if ( graphicChildElem.localName() == "Mark" && format == "ttf" )
1354  {
1355  // mark with ttf format may have a name like ttf://fontFamily
1356  if ( name.startsWith( "ttf://" ) )
1357  name = name.mid( 6 );
1358 
1359  // mark with ttf format has a markIndex element
1360  QDomElement markIndexElem = graphicChildElem.firstChildElement( "MarkIndex" );
1361  if ( markIndexElem.isNull() )
1362  continue;
1363 
1364  bool ok;
1365  int v = markIndexElem.firstChild().nodeValue().toInt( &ok );
1366  if ( !ok || v < 0 )
1367  continue;
1368 
1369  markIndex = v;
1370  }
1371 
1372  found = true;
1373  break;
1374  }
1375 #if 0
1376  else if ( !inlineContentElem.isNull() )
1377  continue; // TODO: not implemented yet
1378 #endif
1379  else
1380  continue;
1381  }
1382 
1383  // if Mark element is present but it doesn't contains neither
1384  // WellKnownName nor OnlineResource nor InlineContent,
1385  // use the default mark (square)
1386  if ( graphicChildElem.localName() == "Mark" )
1387  {
1388  name = "square";
1389  found = true;
1390  break;
1391  }
1392  }
1393 
1394  // if found a valid Mark, check for its Fill and Stroke element
1395  if ( found && graphicChildElem.localName() == "Mark" )
1396  {
1397  // XXX: recursive definition!?! couldn't be dangerous???
1398  // to avoid recursion we handle only simple fill and simple stroke
1399 
1400  // check for simple fill
1401  // Fill element can contain some SvgParameter elements
1402  Qt::BrushStyle markFillStyle;
1403 
1404  QDomElement markFillElem = graphicChildElem.firstChildElement( "Fill" );
1405  if ( fillFromSld( markFillElem, markFillStyle, fillColor ) )
1406  validFill = true;
1407 
1408  // check for simple outline
1409  // Stroke element can contain some SvgParameter elements
1410  Qt::PenStyle borderStyle;
1411  double borderWidth = 1.0, dashOffset = 0.0;
1412  QVector<qreal> customDashPattern;
1413 
1414  QDomElement markStrokeElem = graphicChildElem.firstChildElement( "Stroke" );
1415  if ( lineFromSld( markStrokeElem, borderStyle, borderColor, borderWidth,
1416  0, 0, &customDashPattern, &dashOffset ) )
1417  validBorder = true;
1418  }
1419 
1420  if ( found )
1421  {
1422  // check for Opacity, Size, Rotation, AnchorPoint, Displacement
1423  QDomElement opacityElem = graphicElem.firstChildElement( "Opacity" );
1424  if ( !opacityElem.isNull() )
1425  fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) );
1426 
1427  QDomElement sizeElem = graphicElem.firstChildElement( "Size" );
1428  if ( !sizeElem.isNull() )
1429  {
1430  bool ok;
1431  double v = sizeElem.firstChild().nodeValue().toDouble( &ok );
1432  if ( ok && v > 0 )
1433  size = v;
1434  }
1435 
1436  QString angleFunc;
1437  if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() )
1438  {
1439  bool ok;
1440  double v = angleFunc.toDouble( &ok );
1441  if ( ok )
1442  angle = v;
1443  }
1444 
1445  displacementFromSldElement( graphicElem, offset );
1446  }
1447  }
1448  }
1449 
1450  if ( validFill || validBorder )
1451  {
1452  if ( format == "image/svg+xml" )
1453  {
1454  QgsStringMap map;
1455  map["name"] = name;
1456  map["fill"] = fillColor.name();
1457  map["outline"] = borderColor.name();
1458  map["outline-width"] = QString::number( borderWidth );
1459  if ( !qgsDoubleNear( size, 0.0 ) )
1460  map["size"] = QString::number( size );
1461  if ( !qgsDoubleNear( angle, 0.0 ) )
1462  map["angle"] = QString::number( angle );
1463  if ( !offset.isNull() )
1464  map["offset"] = encodePoint( offset );
1465  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SvgMarker", map ) );
1466  }
1467  else if ( format == "ttf" )
1468  {
1469  QgsStringMap map;
1470  map["font"] = name;
1471  map["chr"] = markIndex;
1472  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1473  if ( size > 0 )
1474  map["size"] = QString::number( size );
1475  if ( !qgsDoubleNear( angle, 0.0 ) )
1476  map["angle"] = QString::number( angle );
1477  if ( !offset.isNull() )
1478  map["offset"] = encodePoint( offset );
1479  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "FontMarker", map ) );
1480  }
1481  }
1482  }
1483 
1484  if ( layers.isEmpty() )
1485  return false;
1486 
1487  layerList << layers;
1488  layers.clear();
1489  return true;
1490 }
1491 
1492 void QgsSymbolLayerV2Utils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, QColor color )
1493 {
1494  QString patternName;
1495  switch ( brushStyle )
1496  {
1497  case Qt::NoBrush:
1498  return;
1499 
1500  case Qt::SolidPattern:
1501  if ( color.isValid() )
1502  {
1503  element.appendChild( createSvgParameterElement( doc, "fill", color.name() ) );
1504  if ( color.alpha() < 255 )
1505  element.appendChild( createSvgParameterElement( doc, "fill-opacity", encodeSldAlpha( color.alpha() ) ) );
1506  }
1507  return;
1508 
1509  case Qt::CrossPattern:
1510  case Qt::DiagCrossPattern:
1511  case Qt::HorPattern:
1512  case Qt::VerPattern:
1513  case Qt::BDiagPattern:
1514  case Qt::FDiagPattern:
1515  case Qt::Dense1Pattern:
1516  case Qt::Dense2Pattern:
1517  case Qt::Dense3Pattern:
1518  case Qt::Dense4Pattern:
1519  case Qt::Dense5Pattern:
1520  case Qt::Dense6Pattern:
1521  case Qt::Dense7Pattern:
1522  patternName = encodeSldBrushStyle( brushStyle );
1523  break;
1524 
1525  default:
1526  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) );
1527  return;
1528  }
1529 
1530  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1531  element.appendChild( graphicFillElem );
1532 
1533  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1534  graphicFillElem.appendChild( graphicElem );
1535 
1536  QColor fillColor = patternName.startsWith( "brush://" ) ? color : QColor();
1537  QColor borderColor = !patternName.startsWith( "brush://" ) ? color : QColor();
1538 
1539  /* Use WellKnownName tag to handle QT brush styles. */
1540  wellKnownMarkerToSld( doc, graphicElem, patternName, fillColor, borderColor, Qt::SolidLine, -1, -1 );
1541 }
1542 
1543 bool QgsSymbolLayerV2Utils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color )
1544 {
1545  QgsDebugMsg( "Entered." );
1546 
1547  brushStyle = Qt::SolidPattern;
1548  color = QColor( "#808080" );
1549 
1550  if ( element.isNull() )
1551  {
1552  brushStyle = Qt::NoBrush;
1553  color = QColor();
1554  return true;
1555  }
1556 
1557  QDomElement graphicFillElem = element.firstChildElement( "GraphicFill" );
1558  // if no GraphicFill element is found, it's a solid fill
1559  if ( graphicFillElem.isNull() )
1560  {
1561  QgsStringMap svgParams = getSvgParameterList( element );
1562  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1563  {
1564  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) );
1565 
1566  if ( it.key() == "fill" )
1567  color = QColor( it.value() );
1568  else if ( it.key() == "fill-opacity" )
1569  color.setAlpha( decodeSldAlpha( it.value() ) );
1570  }
1571  }
1572  else // wellKnown marker
1573  {
1574  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1575  if ( graphicElem.isNull() )
1576  return false; // Graphic is required within GraphicFill
1577 
1578  QString patternName = "square";
1579  QColor fillColor, borderColor;
1580  double borderWidth, size;
1581  Qt::PenStyle borderStyle;
1582  if ( !wellKnownMarkerFromSld( graphicElem, patternName, fillColor, borderColor, borderStyle, borderWidth, size ) )
1583  return false;
1584 
1585  brushStyle = decodeSldBrushStyle( patternName );
1586  if ( brushStyle == Qt::NoBrush )
1587  return false; // unable to decode brush style
1588 
1589  QColor c = patternName.startsWith( "brush://" ) ? fillColor : borderColor;
1590  if ( c.isValid() )
1591  color = c;
1592  }
1593 
1594  return true;
1595 }
1596 
1597 void QgsSymbolLayerV2Utils::lineToSld( QDomDocument &doc, QDomElement &element,
1598  Qt::PenStyle penStyle, QColor color, double width,
1599  const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
1600  const QVector<qreal> *customDashPattern, double dashOffset )
1601 {
1602  QVector<qreal> dashPattern;
1603  const QVector<qreal> *pattern = &dashPattern;
1604 
1605  if ( penStyle == Qt::CustomDashLine && !customDashPattern )
1606  {
1607  element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) );
1608  penStyle = Qt::DashLine;
1609  }
1610 
1611  switch ( penStyle )
1612  {
1613  case Qt::NoPen:
1614  return;
1615 
1616  case Qt::SolidLine:
1617  break;
1618 
1619  case Qt::DashLine:
1620  dashPattern.push_back( 4.0 );
1621  dashPattern.push_back( 2.0 );
1622  break;
1623  case Qt::DotLine:
1624  dashPattern.push_back( 1.0 );
1625  dashPattern.push_back( 2.0 );
1626  break;
1627  case Qt::DashDotLine:
1628  dashPattern.push_back( 4.0 );
1629  dashPattern.push_back( 2.0 );
1630  dashPattern.push_back( 1.0 );
1631  dashPattern.push_back( 2.0 );
1632  break;
1633  case Qt::DashDotDotLine:
1634  dashPattern.push_back( 4.0 );
1635  dashPattern.push_back( 2.0 );
1636  dashPattern.push_back( 1.0 );
1637  dashPattern.push_back( 2.0 );
1638  dashPattern.push_back( 1.0 );
1639  dashPattern.push_back( 2.0 );
1640  break;
1641 
1642  case Qt::CustomDashLine:
1643  Q_ASSERT( customDashPattern );
1644  pattern = customDashPattern;
1645  break;
1646 
1647  default:
1648  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
1649  return;
1650  }
1651 
1652  if ( color.isValid() )
1653  {
1654  element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) );
1655  if ( color.alpha() < 255 )
1656  element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) );
1657  }
1658  if ( width > 0 )
1659  element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) );
1660  if ( penJoinStyle )
1661  element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) );
1662  if ( penCapStyle )
1663  element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) );
1664 
1665  if ( pattern->size() > 0 )
1666  {
1667  element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *pattern ) ) );
1668  if ( !qgsDoubleNear( dashOffset, 0.0 ) )
1669  element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) );
1670  }
1671 }
1672 
1673 
1674 bool QgsSymbolLayerV2Utils::lineFromSld( QDomElement &element,
1675  Qt::PenStyle &penStyle, QColor &color, double &width,
1676  Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle,
1677  QVector<qreal> *customDashPattern, double *dashOffset )
1678 {
1679  QgsDebugMsg( "Entered." );
1680 
1681  penStyle = Qt::SolidLine;
1682  color = QColor( "#000000" );
1683  width = 1;
1684  if ( penJoinStyle )
1685  *penJoinStyle = Qt::BevelJoin;
1686  if ( penCapStyle )
1687  *penCapStyle = Qt::SquareCap;
1688  if ( customDashPattern )
1689  customDashPattern->clear();
1690  if ( dashOffset )
1691  *dashOffset = 0;
1692 
1693  if ( element.isNull() )
1694  {
1695  penStyle = Qt::NoPen;
1696  color = QColor();
1697  return true;
1698  }
1699 
1700  QgsStringMap svgParams = getSvgParameterList( element );
1701  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1702  {
1703  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key() ).arg( it.value() ) );
1704 
1705  if ( it.key() == "stroke" )
1706  {
1707  color = QColor( it.value() );
1708  }
1709  else if ( it.key() == "stroke-opacity" )
1710  {
1711  color.setAlpha( decodeSldAlpha( it.value() ) );
1712  }
1713  else if ( it.key() == "stroke-width" )
1714  {
1715  bool ok;
1716  double w = it.value().toDouble( &ok );
1717  if ( ok )
1718  width = w;
1719  }
1720  else if ( it.key() == "stroke-linejoin" && penJoinStyle )
1721  {
1722  *penJoinStyle = decodeSldLineJoinStyle( it.value() );
1723  }
1724  else if ( it.key() == "stroke-linecap" && penCapStyle )
1725  {
1726  *penCapStyle = decodeSldLineCapStyle( it.value() );
1727  }
1728  else if ( it.key() == "stroke-dasharray" )
1729  {
1730  QVector<qreal> dashPattern = decodeSldRealVector( it.value() );
1731  if ( dashPattern.size() > 0 )
1732  {
1733  // convert the dasharray to one of the QT pen style,
1734  // if no match is found then set pen style to CustomDashLine
1735  bool dashPatternFound = false;
1736 
1737  if ( dashPattern.count() == 2 )
1738  {
1739  if ( dashPattern.at( 0 ) == 4.0 &&
1740  dashPattern.at( 1 ) == 2.0 )
1741  {
1742  penStyle = Qt::DashLine;
1743  dashPatternFound = true;
1744  }
1745  else if ( dashPattern.at( 0 ) == 1.0 &&
1746  dashPattern.at( 1 ) == 2.0 )
1747  {
1748  penStyle = Qt::DotLine;
1749  dashPatternFound = true;
1750  }
1751  }
1752  else if ( dashPattern.count() == 4 )
1753  {
1754  if ( dashPattern.at( 0 ) == 4.0 &&
1755  dashPattern.at( 1 ) == 2.0 &&
1756  dashPattern.at( 2 ) == 1.0 &&
1757  dashPattern.at( 3 ) == 2.0 )
1758  {
1759  penStyle = Qt::DashDotLine;
1760  dashPatternFound = true;
1761  }
1762  }
1763  else if ( dashPattern.count() == 6 )
1764  {
1765  if ( dashPattern.at( 0 ) == 4.0 &&
1766  dashPattern.at( 1 ) == 2.0 &&
1767  dashPattern.at( 2 ) == 1.0 &&
1768  dashPattern.at( 3 ) == 2.0 &&
1769  dashPattern.at( 4 ) == 1.0 &&
1770  dashPattern.at( 5 ) == 2.0 )
1771  {
1772  penStyle = Qt::DashDotDotLine;
1773  dashPatternFound = true;
1774  }
1775  }
1776 
1777  // default case: set pen style to CustomDashLine
1778  if ( !dashPatternFound )
1779  {
1780  if ( customDashPattern )
1781  {
1782  penStyle = Qt::CustomDashLine;
1783  *customDashPattern = dashPattern;
1784  }
1785  else
1786  {
1787  QgsDebugMsg( "custom dash pattern required but not provided. Using default dash pattern." );
1788  penStyle = Qt::DashLine;
1789  }
1790  }
1791  }
1792  }
1793  else if ( it.key() == "stroke-dashoffset" && dashOffset )
1794  {
1795  bool ok;
1796  double d = it.value().toDouble( &ok );
1797  if ( ok )
1798  *dashOffset = d;
1799  }
1800  }
1801 
1802  return true;
1803 }
1804 
1805 void QgsSymbolLayerV2Utils::externalGraphicToSld( QDomDocument &doc, QDomElement &element,
1806  QString path, QString mime,
1807  QColor color, double size )
1808 {
1809  QDomElement externalGraphicElem = doc.createElement( "se:ExternalGraphic" );
1810  element.appendChild( externalGraphicElem );
1811 
1812  createOnlineResourceElement( doc, externalGraphicElem, path, mime );
1813 
1814  //TODO: missing a way to handle svg color. Should use <se:ColorReplacement>
1815  Q_UNUSED( color );
1816 
1817  if ( size >= 0 )
1818  {
1819  QDomElement sizeElem = doc.createElement( "se:Size" );
1820  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
1821  element.appendChild( sizeElem );
1822  }
1823 }
1824 
1826  QString &path, QString &mime,
1827  QColor &color, double &size )
1828 {
1829  QgsDebugMsg( "Entered." );
1830  Q_UNUSED( color );
1831 
1832  QDomElement externalGraphicElem = element.firstChildElement( "ExternalGraphic" );
1833  if ( externalGraphicElem.isNull() )
1834  return false;
1835 
1836  onlineResourceFromSldElement( externalGraphicElem, path, mime );
1837 
1838  QDomElement sizeElem = element.firstChildElement( "Size" );
1839  if ( !sizeElem.isNull() )
1840  {
1841  bool ok;
1842  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
1843  if ( ok )
1844  size = s;
1845  }
1846 
1847  return true;
1848 }
1849 
1850 void QgsSymbolLayerV2Utils::externalMarkerToSld( QDomDocument &doc, QDomElement &element,
1851  QString path, QString format, int *markIndex,
1852  QColor color, double size )
1853 {
1854  QDomElement markElem = doc.createElement( "se:Mark" );
1855  element.appendChild( markElem );
1856 
1857  createOnlineResourceElement( doc, markElem, path, format );
1858 
1859  if ( markIndex )
1860  {
1861  QDomElement markIndexElem = doc.createElement( "se:MarkIndex" );
1862  markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) );
1863  markElem.appendChild( markIndexElem );
1864  }
1865 
1866  // <Fill>
1867  QDomElement fillElem = doc.createElement( "se:Fill" );
1868  fillToSld( doc, fillElem, Qt::SolidPattern, color );
1869  markElem.appendChild( fillElem );
1870 
1871  // <Size>
1872  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
1873  {
1874  QDomElement sizeElem = doc.createElement( "se:Size" );
1875  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
1876  element.appendChild( sizeElem );
1877  }
1878 }
1879 
1881  QString &path, QString &format, int &markIndex,
1882  QColor &color, double &size )
1883 {
1884  QgsDebugMsg( "Entered." );
1885 
1886  color = QColor();
1887  markIndex = -1;
1888  size = -1;
1889 
1890  QDomElement markElem = element.firstChildElement( "Mark" );
1891  if ( markElem.isNull() )
1892  return false;
1893 
1894  onlineResourceFromSldElement( markElem, path, format );
1895 
1896  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
1897  if ( !markIndexElem.isNull() )
1898  {
1899  bool ok;
1900  int i = markIndexElem.firstChild().nodeValue().toInt( &ok );
1901  if ( ok )
1902  markIndex = i;
1903  }
1904 
1905  // <Fill>
1906  QDomElement fillElem = markElem.firstChildElement( "Fill" );
1907  Qt::BrushStyle b = Qt::SolidPattern;
1908  fillFromSld( fillElem, b, color );
1909  // ignore brush style, solid expected
1910 
1911  // <Size>
1912  QDomElement sizeElem = element.firstChildElement( "Size" );
1913  if ( !sizeElem.isNull() )
1914  {
1915  bool ok;
1916  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
1917  if ( ok )
1918  size = s;
1919  }
1920 
1921  return true;
1922 }
1923 
1924 void QgsSymbolLayerV2Utils::wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element,
1925  QString name, QColor color, QColor borderColor,
1926  double borderWidth, double size )
1927 {
1928  wellKnownMarkerToSld( doc, element, name, color, borderColor, Qt::SolidLine, borderWidth, size );
1929 }
1930 
1931 void QgsSymbolLayerV2Utils::wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element,
1932  QString name, QColor color, QColor borderColor, Qt::PenStyle borderStyle,
1933  double borderWidth, double size )
1934 {
1935  QDomElement markElem = doc.createElement( "se:Mark" );
1936  element.appendChild( markElem );
1937 
1938  QDomElement wellKnownNameElem = doc.createElement( "se:WellKnownName" );
1939  wellKnownNameElem.appendChild( doc.createTextNode( name ) );
1940  markElem.appendChild( wellKnownNameElem );
1941 
1942  // <Fill>
1943  if ( color.isValid() )
1944  {
1945  QDomElement fillElem = doc.createElement( "se:Fill" );
1946  fillToSld( doc, fillElem, Qt::SolidPattern, color );
1947  markElem.appendChild( fillElem );
1948  }
1949 
1950  // <Stroke>
1951  if ( borderColor.isValid() )
1952  {
1953  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1954  lineToSld( doc, strokeElem, borderStyle, borderColor, borderWidth );
1955  markElem.appendChild( strokeElem );
1956  }
1957 
1958  // <Size>
1959  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
1960  {
1961  QDomElement sizeElem = doc.createElement( "se:Size" );
1962  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
1963  element.appendChild( sizeElem );
1964  }
1965 }
1966 
1968  QString &name, QColor &color, QColor &borderColor,
1969  double &borderWidth, double &size )
1970 {
1971  Qt::PenStyle borderStyle;
1972  return wellKnownMarkerFromSld( element, name, color, borderColor, borderStyle, borderWidth, size );
1973 }
1974 
1976  QString &name, QColor &color, QColor &borderColor, Qt::PenStyle &borderStyle,
1977  double &borderWidth, double &size )
1978 {
1979  QgsDebugMsg( "Entered." );
1980 
1981  name = "square";
1982  color = QColor();
1983  borderColor = QColor( "#000000" );
1984  borderWidth = 1;
1985  size = 6;
1986 
1987  QDomElement markElem = element.firstChildElement( "Mark" );
1988  if ( markElem.isNull() )
1989  return false;
1990 
1991  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
1992  if ( !wellKnownNameElem.isNull() )
1993  {
1994  name = wellKnownNameElem.firstChild().nodeValue();
1995  QgsDebugMsg( "found Mark with well known name: " + name );
1996  }
1997 
1998  // <Fill>
1999  QDomElement fillElem = markElem.firstChildElement( "Fill" );
2000  Qt::BrushStyle b = Qt::SolidPattern;
2001  fillFromSld( fillElem, b, color );
2002  // ignore brush style, solid expected
2003 
2004  // <Stroke>
2005  QDomElement strokeElem = markElem.firstChildElement( "Stroke" );
2006  lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
2007  // ignore border style, solid expected
2008 
2009  // <Size>
2010  QDomElement sizeElem = element.firstChildElement( "Size" );
2011  if ( !sizeElem.isNull() )
2012  {
2013  bool ok;
2014  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2015  if ( ok )
2016  size = s;
2017  }
2018 
2019  return true;
2020 }
2021 
2022 void QgsSymbolLayerV2Utils::createRotationElement( QDomDocument &doc, QDomElement &element, QString rotationFunc )
2023 {
2024  if ( !rotationFunc.isEmpty() )
2025  {
2026  QDomElement rotationElem = doc.createElement( "se:Rotation" );
2027  createFunctionElement( doc, rotationElem, rotationFunc );
2028  element.appendChild( rotationElem );
2029  }
2030 }
2031 
2032 bool QgsSymbolLayerV2Utils::rotationFromSldElement( QDomElement &element, QString &rotationFunc )
2033 {
2034  QDomElement rotationElem = element.firstChildElement( "Rotation" );
2035  if ( !rotationElem.isNull() )
2036  {
2037  return functionFromSldElement( rotationElem, rotationFunc );
2038  }
2039  return true;
2040 }
2041 
2042 
2043 void QgsSymbolLayerV2Utils::createOpacityElement( QDomDocument &doc, QDomElement &element, QString alphaFunc )
2044 {
2045  if ( !alphaFunc.isEmpty() )
2046  {
2047  QDomElement opacityElem = doc.createElement( "se:Opacity" );
2048  createFunctionElement( doc, opacityElem, alphaFunc );
2049  element.appendChild( opacityElem );
2050  }
2051 }
2052 
2053 bool QgsSymbolLayerV2Utils::opacityFromSldElement( QDomElement &element, QString &alphaFunc )
2054 {
2055  QDomElement opacityElem = element.firstChildElement( "Opacity" );
2056  if ( !opacityElem.isNull() )
2057  {
2058  return functionFromSldElement( opacityElem, alphaFunc );
2059  }
2060  return true;
2061 }
2062 
2063 void QgsSymbolLayerV2Utils::createDisplacementElement( QDomDocument &doc, QDomElement &element, QPointF offset )
2064 {
2065  if ( offset.isNull() )
2066  return;
2067 
2068  QDomElement displacementElem = doc.createElement( "se:Displacement" );
2069  element.appendChild( displacementElem );
2070 
2071  QDomElement dispXElem = doc.createElement( "se:DisplacementX" );
2072  dispXElem.appendChild( doc.createTextNode( QString::number( offset.x() ) ) );
2073 
2074  QDomElement dispYElem = doc.createElement( "se:DisplacementY" );
2075  dispYElem.appendChild( doc.createTextNode( QString::number( offset.y() ) ) );
2076 
2077  displacementElem.appendChild( dispXElem );
2078  displacementElem.appendChild( dispYElem );
2079 }
2080 
2081 bool QgsSymbolLayerV2Utils::displacementFromSldElement( QDomElement &element, QPointF &offset )
2082 {
2083  offset = QPointF( 0, 0 );
2084 
2085  QDomElement displacementElem = element.firstChildElement( "Displacement" );
2086  if ( displacementElem.isNull() )
2087  return true;
2088 
2089  QDomElement dispXElem = displacementElem.firstChildElement( "DisplacementX" );
2090  if ( !dispXElem.isNull() )
2091  {
2092  bool ok;
2093  double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok );
2094  if ( ok )
2095  offset.setX( offsetX );
2096  }
2097 
2098  QDomElement dispYElem = displacementElem.firstChildElement( "DisplacementY" );
2099  if ( !dispYElem.isNull() )
2100  {
2101  bool ok;
2102  double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok );
2103  if ( ok )
2104  offset.setY( offsetY );
2105  }
2106 
2107  return true;
2108 }
2109 
2110 void QgsSymbolLayerV2Utils::labelTextToSld( QDomDocument &doc, QDomElement &element,
2111  QString label, QFont font,
2112  QColor color, double size )
2113 {
2114  QDomElement labelElem = doc.createElement( "se:Label" );
2115  labelElem.appendChild( doc.createTextNode( label ) );
2116  element.appendChild( labelElem );
2117 
2118  QDomElement fontElem = doc.createElement( "se:Font" );
2119  element.appendChild( fontElem );
2120 
2121  fontElem.appendChild( createSvgParameterElement( doc, "font-family", font.family() ) );
2122 #if 0
2123  fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) );
2124  fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) );
2125 #endif
2126  fontElem.appendChild( createSvgParameterElement( doc, "font-size", QString::number( size ) ) );
2127 
2128  // <Fill>
2129  if ( color.isValid() )
2130  {
2131  QDomElement fillElem = doc.createElement( "Fill" );
2132  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2133  element.appendChild( fillElem );
2134  }
2135 }
2136 
2137 QString QgsSymbolLayerV2Utils::ogrFeatureStylePen( double width, double mmScaleFactor, double mapUnitScaleFactor, const QColor& c,
2138  Qt::PenJoinStyle joinStyle,
2139  Qt::PenCapStyle capStyle,
2140  double offset,
2141  const QVector<qreal>* dashPattern )
2142 {
2143  QString penStyle;
2144  penStyle.append( "PEN(" );
2145  penStyle.append( "c:" );
2146  penStyle.append( c.name() );
2147  penStyle.append( ",w:" );
2148  //dxf driver writes ground units as mm? Should probably be changed in ogr
2149  penStyle.append( QString::number( width * mmScaleFactor ) );
2150  penStyle.append( "mm" );
2151 
2152  //dash dot vector
2153  if ( dashPattern && dashPattern->size() > 0 )
2154  {
2155  penStyle.append( ",p:\"" );
2156  QVector<qreal>::const_iterator pIt = dashPattern->constBegin();
2157  for ( ; pIt != dashPattern->constEnd(); ++pIt )
2158  {
2159  if ( pIt != dashPattern->constBegin() )
2160  {
2161  penStyle.append( " " );
2162  }
2163  penStyle.append( QString::number( *pIt * mapUnitScaleFactor ) );
2164  penStyle.append( "g" );
2165  }
2166  penStyle.append( "\"" );
2167  }
2168 
2169  //cap
2170  penStyle.append( ",cap:" );
2171  switch ( capStyle )
2172  {
2173  case Qt::SquareCap:
2174  penStyle.append( "p" );
2175  break;
2176  case Qt::RoundCap:
2177  penStyle.append( "r" );
2178  break;
2179  case Qt::FlatCap:
2180  default:
2181  penStyle.append( "b" );
2182  }
2183 
2184  //join
2185  penStyle.append( ",j:" );
2186  switch ( joinStyle )
2187  {
2188  case Qt::BevelJoin:
2189  penStyle.append( "b" );
2190  break;
2191  case Qt::RoundJoin:
2192  penStyle.append( "r" );
2193  break;
2194  case Qt::MiterJoin:
2195  default:
2196  penStyle.append( "m" );
2197  }
2198 
2199  //offset
2200  if ( !qgsDoubleNear( offset, 0.0 ) )
2201  {
2202  penStyle.append( ",dp:" );
2203  penStyle.append( QString::number( offset * mapUnitScaleFactor ) );
2204  penStyle.append( "g" );
2205  }
2206 
2207  penStyle.append( ")" );
2208  return penStyle;
2209 }
2210 
2211 QString QgsSymbolLayerV2Utils::ogrFeatureStyleBrush( const QColor& fillColor )
2212 {
2213  QString brushStyle;
2214  brushStyle.append( "BRUSH(" );
2215  brushStyle.append( "fc:" );
2216  brushStyle.append( fillColor.name() );
2217  brushStyle.append( ")" );
2218  return brushStyle;
2219 }
2220 
2221 void QgsSymbolLayerV2Utils::createGeometryElement( QDomDocument &doc, QDomElement &element, QString geomFunc )
2222 {
2223  if ( geomFunc.isEmpty() )
2224  return;
2225 
2226  QDomElement geometryElem = doc.createElement( "Geometry" );
2227  element.appendChild( geometryElem );
2228 
2229  /* About using a function withing the Geometry tag.
2230  *
2231  * The SLD specification <= 1.1 is vague:
2232  * "In principle, a fixed geometry could be defined using GML or
2233  * operators could be defined for computing the geometry from
2234  * references or literals. However, using a feature property directly
2235  * is by far the most commonly useful method."
2236  *
2237  * Even if it seems that specs should take care all the possible cases,
2238  * looking at the XML schema fragment that encodes the Geometry element,
2239  * it has to be a PropertyName element:
2240  * <xsd:element name="Geometry">
2241  * <xsd:complexType>
2242  * <xsd:sequence>
2243  * <xsd:element ref="ogc:PropertyName"/>
2244  * </xsd:sequence>
2245  * </xsd:complexType>
2246  * </xsd:element>
2247  *
2248  * Anyway we will use a ogc:Function to handle geometry transformations
2249  * like offset, centroid, ...
2250  */
2251 
2252  createFunctionElement( doc, geometryElem, geomFunc );
2253 }
2254 
2255 bool QgsSymbolLayerV2Utils::geometryFromSldElement( QDomElement &element, QString &geomFunc )
2256 {
2257  QDomElement geometryElem = element.firstChildElement( "Geometry" );
2258  if ( geometryElem.isNull() )
2259  return true;
2260 
2261  return functionFromSldElement( geometryElem, geomFunc );
2262 }
2263 
2264 bool QgsSymbolLayerV2Utils::createFunctionElement( QDomDocument &doc, QDomElement &element, QString function )
2265 {
2266  // let's use QgsExpression to generate the SLD for the function
2267  QgsExpression expr( function );
2268  if ( expr.hasParserError() )
2269  {
2270  element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
2271  return false;
2272  }
2273  QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
2274  if ( !filterElem.isNull() )
2275  element.appendChild( filterElem );
2276  return true;
2277 }
2278 
2279 bool QgsSymbolLayerV2Utils::functionFromSldElement( QDomElement &element, QString &function )
2280 {
2281  QgsDebugMsg( "Entered." );
2282 
2284  if ( !expr )
2285  return false;
2286 
2287  bool valid = !expr->hasParserError();
2288  if ( !valid )
2289  {
2290  QgsDebugMsg( "parser error: " + expr->parserErrorString() );
2291  }
2292  else
2293  {
2294  function = expr->expression();
2295  }
2296 
2297  delete expr;
2298  return valid;
2299 }
2300 
2301 void QgsSymbolLayerV2Utils::createOnlineResourceElement( QDomDocument &doc, QDomElement &element,
2302  QString path, QString format )
2303 {
2304  // get resource url or relative path
2305  QString url = symbolPathToName( path );
2306  QDomElement onlineResourceElem = doc.createElement( "se:OnlineResource" );
2307  onlineResourceElem.setAttribute( "xlink:type", "simple" );
2308  onlineResourceElem.setAttribute( "xlink:href", url );
2309  element.appendChild( onlineResourceElem );
2310 
2311  QDomElement formatElem = doc.createElement( "se:Format" );
2312  formatElem.appendChild( doc.createTextNode( format ) );
2313  element.appendChild( formatElem );
2314 }
2315 
2316 bool QgsSymbolLayerV2Utils::onlineResourceFromSldElement( QDomElement &element, QString &path, QString &format )
2317 {
2318  QgsDebugMsg( "Entered." );
2319 
2320  QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
2321  if ( onlineResourceElem.isNull() )
2322  return false;
2323 
2324  path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
2325 
2326  QDomElement formatElem = element.firstChildElement( "Format" );
2327  if ( formatElem.isNull() )
2328  return false; // OnlineResource requires a Format sibling element
2329 
2330  format = formatElem.firstChild().nodeValue();
2331  return true;
2332 }
2333 
2334 
2335 QDomElement QgsSymbolLayerV2Utils::createSvgParameterElement( QDomDocument &doc, QString name, QString value )
2336 {
2337  QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
2338  nodeElem.setAttribute( "name", name );
2339  nodeElem.appendChild( doc.createTextNode( value ) );
2340  return nodeElem;
2341 }
2342 
2344 {
2345  QgsStringMap params;
2346 
2347  QDomElement paramElem = element.firstChildElement();
2348  while ( !paramElem.isNull() )
2349  {
2350  if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
2351  {
2352  QString name = paramElem.attribute( "name" );
2353  QString value = paramElem.firstChild().nodeValue();
2354 
2355  if ( !name.isEmpty() && !value.isEmpty() )
2356  params[ name ] = value;
2357  }
2358 
2359  paramElem = paramElem.nextSiblingElement();
2360  }
2361 
2362  return params;
2363 }
2364 
2365 QDomElement QgsSymbolLayerV2Utils::createVendorOptionElement( QDomDocument &doc, QString name, QString value )
2366 {
2367  QDomElement nodeElem = doc.createElement( "VendorOption" );
2368  nodeElem.setAttribute( "name", name );
2369  nodeElem.appendChild( doc.createTextNode( value ) );
2370  return nodeElem;
2371 }
2372 
2374 {
2375  QgsStringMap params;
2376 
2377  QDomElement paramElem = element.firstChildElement( "VendorOption" );
2378  while ( !paramElem.isNull() )
2379  {
2380  QString name = paramElem.attribute( "name" );
2381  QString value = paramElem.firstChild().nodeValue();
2382 
2383  if ( !name.isEmpty() && !value.isEmpty() )
2384  params[ name ] = value;
2385 
2386  paramElem = paramElem.nextSiblingElement( "VendorOption" );
2387  }
2388 
2389  return params;
2390 }
2391 
2392 
2394 {
2395  QgsStringMap props;
2396  QDomElement e = element.firstChildElement();
2397  while ( !e.isNull() )
2398  {
2399  if ( e.tagName() != "prop" )
2400  {
2401  QgsDebugMsg( "unknown tag " + e.tagName() );
2402  }
2403  else
2404  {
2405  QString propKey = e.attribute( "k" );
2406  QString propValue = e.attribute( "v" );
2407  props[propKey] = propValue;
2408  }
2409  e = e.nextSiblingElement();
2410  }
2411  return props;
2412 }
2413 
2414 
2415 void QgsSymbolLayerV2Utils::saveProperties( QgsStringMap props, QDomDocument& doc, QDomElement& element )
2416 {
2417  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
2418  {
2419  QDomElement propEl = doc.createElement( "prop" );
2420  propEl.setAttribute( "k", it.key() );
2421  propEl.setAttribute( "v", it.value() );
2422  element.appendChild( propEl );
2423  }
2424 }
2425 
2427 {
2428  // go through symbols one-by-one and load them
2429 
2430  QgsSymbolV2Map symbols;
2431  QDomElement e = element.firstChildElement();
2432 
2433  while ( !e.isNull() )
2434  {
2435  if ( e.tagName() == "symbol" )
2436  {
2438  if ( symbol != NULL )
2439  symbols.insert( e.attribute( "name" ), symbol );
2440  }
2441  else
2442  {
2443  QgsDebugMsg( "unknown tag: " + e.tagName() );
2444  }
2445  e = e.nextSiblingElement();
2446  }
2447 
2448 
2449  // now walk through the list of symbols and find those prefixed with @
2450  // these symbols are sub-symbols of some other symbol layers
2451  // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo"
2452  QStringList subsymbols;
2453 
2454  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2455  {
2456  if ( it.key()[0] != '@' )
2457  continue;
2458 
2459  // add to array (for deletion)
2460  subsymbols.append( it.key() );
2461 
2462  QStringList parts = it.key().split( "@" );
2463  if ( parts.count() < 3 )
2464  {
2465  QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
2466  delete it.value(); // we must delete it
2467  continue; // some invalid syntax
2468  }
2469  QString symname = parts[1];
2470  int symlayer = parts[2].toInt();
2471 
2472  if ( !symbols.contains( symname ) )
2473  {
2474  QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
2475  delete it.value(); // we must delete it
2476  continue;
2477  }
2478 
2479  QgsSymbolV2* sym = symbols[symname];
2480  if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
2481  {
2482  QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
2483  delete it.value(); // we must delete it
2484  continue;
2485  }
2486 
2487  // set subsymbol takes ownership
2488  bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
2489  if ( !res )
2490  {
2491  QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
2492  }
2493 
2494 
2495  }
2496 
2497  // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
2498  for ( int i = 0; i < subsymbols.count(); i++ )
2499  symbols.take( subsymbols[i] );
2500 
2501  return symbols;
2502 }
2503 
2504 QDomElement QgsSymbolLayerV2Utils::saveSymbols( QgsSymbolV2Map& symbols, QString tagName, QDomDocument& doc )
2505 {
2506  QDomElement symbolsElem = doc.createElement( tagName );
2507 
2508  // save symbols
2509  for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its )
2510  {
2511  QDomElement symEl = saveSymbol( its.key(), its.value(), doc );
2512  symbolsElem.appendChild( symEl );
2513  }
2514 
2515  return symbolsElem;
2516 }
2517 
2519 {
2520  foreach ( QString name, symbols.keys() )
2521  {
2522  delete symbols.value( name );
2523  }
2524  symbols.clear();
2525 }
2526 
2527 
2529 {
2530  QString rampType = element.attribute( "type" );
2531 
2532  // parse properties
2534 
2535  if ( rampType == "gradient" )
2536  return QgsVectorGradientColorRampV2::create( props );
2537  else if ( rampType == "random" )
2538  return QgsVectorRandomColorRampV2::create( props );
2539  else if ( rampType == "colorbrewer" )
2541  else if ( rampType == "cpt-city" )
2542  return QgsCptCityColorRampV2::create( props );
2543  else
2544  {
2545  QgsDebugMsg( "unknown colorramp type " + rampType );
2546  return NULL;
2547  }
2548 }
2549 
2550 
2551 QDomElement QgsSymbolLayerV2Utils::saveColorRamp( QString name, QgsVectorColorRampV2* ramp, QDomDocument& doc )
2552 {
2553  QDomElement rampEl = doc.createElement( "colorramp" );
2554  rampEl.setAttribute( "type", ramp->type() );
2555  rampEl.setAttribute( "name", name );
2556 
2557  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2558  return rampEl;
2559 }
2560 
2561 // parse color definition with format "rgb(0,0,0)" or "0,0,0"
2562 // should support other formats like FFFFFF
2563 QColor QgsSymbolLayerV2Utils::parseColor( QString colorStr )
2564 {
2565  if ( colorStr.startsWith( "rgb(" ) )
2566  {
2567  colorStr = colorStr.mid( 4, colorStr.size() - 5 ).trimmed();
2568  }
2569  QStringList p = colorStr.split( QChar( ',' ) );
2570  if ( p.count() != 3 )
2571  return QColor();
2572  return QColor( p[0].toInt(), p[1].toInt(), p[2].toInt() );
2573 }
2574 
2576 {
2577 
2578  if ( u == QgsSymbolV2::MM )
2579  {
2580  return c.scaleFactor();
2581  }
2582  else //QgsSymbol::MapUnit
2583  {
2584  double mup = c.mapToPixel().mapUnitsPerPixel();
2585  if ( mup > 0 )
2586  {
2587  return 1.0 / mup;
2588  }
2589  else
2590  {
2591  return 1.0;
2592  }
2593  }
2594 }
2595 
2597 {
2598  if ( u == QgsSymbolV2::MM )
2599  {
2600  return ( c.scaleFactor() * c.rasterScaleFactor() );
2601  }
2602  else //QgsSymbol::MapUnit
2603  {
2604  double mup = c.mapToPixel().mapUnitsPerPixel();
2605  if ( mup > 0 )
2606  {
2607  return c.rasterScaleFactor() / c.mapToPixel().mapUnitsPerPixel();
2608  }
2609  else
2610  {
2611  return 1.0;
2612  }
2613  }
2614 }
2615 
2617 {
2618  QgsRenderContext context;
2619  context.setPainter( p );
2620  context.setRasterScaleFactor( 1.0 );
2621  if ( p && p->device() )
2622  {
2623  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
2624  }
2625  else
2626  {
2627  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
2628  }
2629  return context;
2630 }
2631 
2632 void QgsSymbolLayerV2Utils::multiplyImageOpacity( QImage* image, qreal alpha )
2633 {
2634  if ( !image )
2635  {
2636  return;
2637  }
2638 
2639  QRgb myRgb;
2640  QImage::Format format = image->format();
2641  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
2642  {
2643  QgsDebugMsg( "no alpha channel." );
2644  return;
2645  }
2646 
2647  //change the alpha component of every pixel
2648  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
2649  {
2650  QRgb* scanLine = ( QRgb* )image->scanLine( heightIndex );
2651  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
2652  {
2653  myRgb = scanLine[widthIndex];
2654  if ( format == QImage::Format_ARGB32_Premultiplied )
2655  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
2656  else
2657  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
2658  }
2659  }
2660 }
2661 
2662 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, const QRect& rect, int radius, bool alphaOnly )
2663 {
2664  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
2665  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
2666  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
2667 
2668  if ( image.format() != QImage::Format_ARGB32_Premultiplied
2669  && image.format() != QImage::Format_RGB32 )
2670  {
2671  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
2672  }
2673 
2674  int r1 = rect.top();
2675  int r2 = rect.bottom();
2676  int c1 = rect.left();
2677  int c2 = rect.right();
2678 
2679  int bpl = image.bytesPerLine();
2680  int rgba[4];
2681  unsigned char* p;
2682 
2683  int i1 = 0;
2684  int i2 = 3;
2685 
2686  if ( alphaOnly ) // this seems to only work right for a black color
2687  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
2688 
2689  for ( int col = c1; col <= c2; col++ )
2690  {
2691  p = image.scanLine( r1 ) + col * 4;
2692  for ( int i = i1; i <= i2; i++ )
2693  rgba[i] = p[i] << 4;
2694 
2695  p += bpl;
2696  for ( int j = r1; j < r2; j++, p += bpl )
2697  for ( int i = i1; i <= i2; i++ )
2698  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
2699  }
2700 
2701  for ( int row = r1; row <= r2; row++ )
2702  {
2703  p = image.scanLine( row ) + c1 * 4;
2704  for ( int i = i1; i <= i2; i++ )
2705  rgba[i] = p[i] << 4;
2706 
2707  p += 4;
2708  for ( int j = c1; j < c2; j++, p += 4 )
2709  for ( int i = i1; i <= i2; i++ )
2710  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
2711  }
2712 
2713  for ( int col = c1; col <= c2; col++ )
2714  {
2715  p = image.scanLine( r2 ) + col * 4;
2716  for ( int i = i1; i <= i2; i++ )
2717  rgba[i] = p[i] << 4;
2718 
2719  p -= bpl;
2720  for ( int j = r1; j < r2; j++, p -= bpl )
2721  for ( int i = i1; i <= i2; i++ )
2722  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
2723  }
2724 
2725  for ( int row = r1; row <= r2; row++ )
2726  {
2727  p = image.scanLine( row ) + c2 * 4;
2728  for ( int i = i1; i <= i2; i++ )
2729  rgba[i] = p[i] << 4;
2730 
2731  p -= 4;
2732  for ( int j = c1; j < c2; j++, p -= 4 )
2733  for ( int i = i1; i <= i2; i++ )
2734  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
2735  }
2736 }
2737 
2738 #if 0
2739 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
2740 {
2741  switch ( lhs.type() )
2742  {
2743  case QVariant::Int:
2744  return lhs.toInt() < rhs.toInt();
2745  case QVariant::UInt:
2746  return lhs.toUInt() < rhs.toUInt();
2747  case QVariant::LongLong:
2748  return lhs.toLongLong() < rhs.toLongLong();
2749  case QVariant::ULongLong:
2750  return lhs.toULongLong() < rhs.toULongLong();
2751  case QVariant::Double:
2752  return lhs.toDouble() < rhs.toDouble();
2753  case QVariant::Char:
2754  return lhs.toChar() < rhs.toChar();
2755  case QVariant::Date:
2756  return lhs.toDate() < rhs.toDate();
2757  case QVariant::Time:
2758  return lhs.toTime() < rhs.toTime();
2759  case QVariant::DateTime:
2760  return lhs.toDateTime() < rhs.toDateTime();
2761  default:
2762  return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
2763  }
2764 }
2765 
2766 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
2767 {
2768  return ! _QVariantLessThan( lhs, rhs );
2769 }
2770 #endif
2771 
2772 void QgsSymbolLayerV2Utils::sortVariantList( QList<QVariant>& list, Qt::SortOrder order )
2773 {
2774  if ( order == Qt::AscendingOrder )
2775  {
2776  //qSort( list.begin(), list.end(), _QVariantLessThan );
2777  qSort( list.begin(), list.end(), qgsVariantLessThan );
2778  }
2779  else // Qt::DescendingOrder
2780  {
2781  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
2782  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
2783  }
2784 }
2785 
2786 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance )
2787 {
2788  double dx = directionPoint.x() - startPoint.x();
2789  double dy = directionPoint.y() - startPoint.y();
2790  double length = sqrt( dx * dx + dy * dy );
2791  double scaleFactor = distance / length;
2792  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
2793 }
2794 
2795 
2797 {
2798  // copied from QgsMarkerCatalogue - TODO: unify
2799  QStringList list;
2800  QStringList svgPaths = QgsApplication::svgPaths();
2801 
2802  for ( int i = 0; i < svgPaths.size(); i++ )
2803  {
2804  QDir dir( svgPaths[i] );
2805  foreach ( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
2806  {
2807  svgPaths.insert( i + 1, dir.path() + "/" + item );
2808  }
2809 
2810  foreach ( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
2811  {
2812  // TODO test if it is correct SVG
2813  list.append( dir.path() + "/" + item );
2814  }
2815  }
2816  return list;
2817 }
2818 
2819 // Stripped down version of listSvgFiles() for specified directory
2820 QStringList QgsSymbolLayerV2Utils::listSvgFilesAt( QString directory )
2821 {
2822  // TODO anything that applies for the listSvgFiles() applies this also
2823 
2824  QStringList list;
2825  QStringList svgPaths;
2826  svgPaths.append( directory );
2827 
2828  for ( int i = 0; i < svgPaths.size(); i++ )
2829  {
2830  QDir dir( svgPaths[i] );
2831  foreach ( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
2832  {
2833  svgPaths.insert( i + 1, dir.path() + "/" + item );
2834  }
2835 
2836  foreach ( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
2837  {
2838  list.append( dir.path() + "/" + item );
2839  }
2840  }
2841  return list;
2842 
2843 }
2844 
2846 {
2847  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
2848 
2849  // we might have a full path...
2850  if ( QFile( name ).exists() )
2851  return QFileInfo( name ).canonicalFilePath();
2852 
2853  // or it might be an url...
2854  if ( name.contains( "://" ) )
2855  {
2856  QUrl url( name );
2857  if ( url.isValid() && !url.scheme().isEmpty() )
2858  {
2859  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
2860  {
2861  // it's a url to a local file
2862  name = url.toLocalFile();
2863  if ( QFile( name ).exists() )
2864  {
2865  return QFileInfo( name ).canonicalFilePath();
2866  }
2867  }
2868  else
2869  {
2870  // it's a url pointing to a online resource
2871  return name;
2872  }
2873  }
2874  }
2875 
2876  // SVG symbol not found - probably a relative path was used
2877 
2878  QStringList svgPaths = QgsApplication::svgPaths();
2879  for ( int i = 0; i < svgPaths.size(); i++ )
2880  {
2881  QString svgPath = svgPaths[i];
2882  if ( svgPath.endsWith( QString( "/" ) ) )
2883  {
2884  svgPath.chop( 1 );
2885  }
2886 
2887  QgsDebugMsg( "SvgPath: " + svgPath );
2888  QFileInfo myInfo( name );
2889  QString myFileName = myInfo.fileName(); // foo.svg
2890  QString myLowestDir = myInfo.dir().dirName();
2891  QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : "/" + myLowestDir ) + "/" + myFileName;
2892 
2893  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
2894  if ( QFile( myLocalPath ).exists() )
2895  {
2896  QgsDebugMsg( "Svg found in alternative path" );
2897  return QFileInfo( myLocalPath ).canonicalFilePath();
2898  }
2899  else if ( myInfo.isRelative() )
2900  {
2901  QFileInfo pfi( QgsProject::instance()->fileName() );
2902  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
2903  if ( pfi.exists() && QFile( alternatePath ).exists() )
2904  {
2905  QgsDebugMsg( "Svg found in alternative path" );
2906  return QFileInfo( alternatePath ).canonicalFilePath();
2907  }
2908  else
2909  {
2910  QgsDebugMsg( "Svg not found in project path" );
2911  }
2912  }
2913  else
2914  {
2915  //couldnt find the file, no happy ending :-(
2916  QgsDebugMsg( "Computed alternate path but no svg there either" );
2917  }
2918  }
2919  return QString();
2920 }
2921 
2923 {
2924  // copied from QgsSymbol::writeXML
2925 
2926  QFileInfo fi( path );
2927  if ( !fi.exists() )
2928  return path;
2929 
2930  path = fi.canonicalFilePath();
2931 
2932  QStringList svgPaths = QgsApplication::svgPaths();
2933 
2934  bool isInSvgPathes = false;
2935  for ( int i = 0; i < svgPaths.size(); i++ )
2936  {
2937  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
2938 
2939  if ( !dir.isEmpty() && path.startsWith( dir ) )
2940  {
2941  path = path.mid( dir.size() + 1 );
2942  isInSvgPathes = true;
2943  break;
2944  }
2945  }
2946 
2947  if ( isInSvgPathes )
2948  return path;
2949 
2950  return QgsProject::instance()->writePath( path );
2951 }
2952 
2953 QPointF QgsSymbolLayerV2Utils::polygonCentroid( const QPolygonF& points )
2954 {
2955  //Calculate the centroid of points
2956  double cx = 0, cy = 0;
2957  double area, sum = 0;
2958  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
2959  {
2960  const QPointF& p1 = points[i];
2961  const QPointF& p2 = points[j];
2962  area = p1.x() * p2.y() - p1.y() * p2.x();
2963  sum += area;
2964  cx += ( p1.x() + p2.x() ) * area;
2965  cy += ( p1.y() + p2.y() ) * area;
2966  }
2967  sum *= 3.0;
2968  cx /= sum;
2969  cy /= sum;
2970 
2971  return QPointF( cx, cy );
2972 }
2973 
2974 
2976 {
2977  if ( fieldOrExpression.isEmpty() )
2978  return 0;
2979 
2980  QgsExpression* expr = new QgsExpression( fieldOrExpression );
2981  if ( !expr->hasParserError() )
2982  return expr;
2983 
2984  // now try with quoted field name
2985  delete expr;
2986  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
2987  Q_ASSERT( !expr2->hasParserError() );
2988  return expr2;
2989 }
2990 
2992 {
2993  const QgsExpression::Node* n = expression->rootNode();
2994 
2995  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
2996  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
2997 
2998  return expression->expression();
2999 }