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