QGIS API Documentation  2.12.0-Lyon
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 "qgspainteffect.h"
24 #include "qgspainteffectregistry.h"
25 #include "qgsapplication.h"
26 #include "qgsproject.h"
27 #include "qgsogcutils.h"
28 #include "qgslogger.h"
29 #include "qgsrendercontext.h"
30 
31 #include <QColor>
32 #include <QFont>
33 #include <QDomDocument>
34 #include <QDomNode>
35 #include <QDomElement>
36 #include <QIcon>
37 #include <QPainter>
38 #include <QSettings>
39 #include <QRegExp>
40 #include <QPicture>
41 
43 {
44  return QString( "%1,%2,%3,%4" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ).arg( color.alpha() );
45 }
46 
48 {
49  QStringList lst = str.split( "," );
50  if ( lst.count() < 3 )
51  {
52  return QColor( str );
53  }
54  int red, green, blue, alpha;
55  red = lst[0].toInt();
56  green = lst[1].toInt();
57  blue = lst[2].toInt();
58  alpha = 255;
59  if ( lst.count() > 3 )
60  {
61  alpha = lst[3].toInt();
62  }
63  return QColor( red, green, blue, alpha );
64 }
65 
67 {
68  return QString::number( alpha / 255.0, 'f', 2 );
69 }
70 
72 {
73  bool ok;
74  double alpha = str.toDouble( &ok );
75  if ( !ok || alpha > 1 )
76  alpha = 255;
77  else if ( alpha < 0 )
78  alpha = 0;
79  return alpha * 255;
80 }
81 
83 {
84  switch ( style )
85  {
86  case QFont::StyleNormal: return "normal";
87  case QFont::StyleItalic: return "italic";
88  case QFont::StyleOblique: return "oblique";
89  default: return "";
90  }
91 }
92 
94 {
95  if ( str == "normal" ) return QFont::StyleNormal;
96  if ( str == "italic" ) return QFont::StyleItalic;
97  if ( str == "oblique" ) return QFont::StyleOblique;
98  return QFont::StyleNormal;
99 }
100 
102 {
103  if ( weight == 50 ) return "normal";
104  if ( weight == 75 ) return "bold";
105 
106  // QFont::Weight is between 0 and 99
107  // CSS font-weight is between 100 and 900
108  if ( weight < 0 ) return "100";
109  if ( weight > 99 ) return "900";
110  return QString::number( weight * 800 / 99 + 100 );
111 }
112 
114 {
115  bool ok;
116  int weight = str.toInt( &ok );
117  if ( !ok ) return ( int ) QFont::Normal;
118 
119  // CSS font-weight is between 100 and 900
120  // QFont::Weight is between 0 and 99
121  if ( weight > 900 ) return 99;
122  if ( weight < 100 ) return 0;
123  return ( weight - 100 ) * 99 / 800;
124 }
125 
127 {
128  switch ( style )
129  {
130  case Qt::NoPen: return "no";
131  case Qt::SolidLine: return "solid";
132  case Qt::DashLine: return "dash";
133  case Qt::DotLine: return "dot";
134  case Qt::DashDotLine: return "dash dot";
135  case Qt::DashDotDotLine: return "dash dot dot";
136  default: return "???";
137  }
138 }
139 
141 {
142  if ( str == "no" ) return Qt::NoPen;
143  if ( str == "solid" ) return Qt::SolidLine;
144  if ( str == "dash" ) return Qt::DashLine;
145  if ( str == "dot" ) return Qt::DotLine;
146  if ( str == "dash dot" ) return Qt::DashDotLine;
147  if ( str == "dash dot dot" ) return Qt::DashDotDotLine;
148  return Qt::SolidLine;
149 }
150 
152 {
153  switch ( style )
154  {
155  case Qt::BevelJoin: return "bevel";
156  case Qt::MiterJoin: return "miter";
157  case Qt::RoundJoin: return "round";
158  default: return "???";
159  }
160 }
161 
163 {
164  if ( str == "bevel" ) return Qt::BevelJoin;
165  if ( str == "miter" ) return Qt::MiterJoin;
166  if ( str == "round" ) return Qt::RoundJoin;
167  return Qt::BevelJoin;
168 }
169 
171 {
172  switch ( style )
173  {
174  case Qt::BevelJoin: return "bevel";
175  case Qt::MiterJoin: return "mitre";
176  case Qt::RoundJoin: return "round";
177  default: return "";
178  }
179 }
180 
182 {
183  if ( str == "bevel" ) return Qt::BevelJoin;
184  if ( str == "mitre" ) return Qt::MiterJoin;
185  if ( str == "round" ) return Qt::RoundJoin;
186  return Qt::BevelJoin;
187 }
188 
190 {
191  switch ( style )
192  {
193  case Qt::SquareCap: return "square";
194  case Qt::FlatCap: return "flat";
195  case Qt::RoundCap: return "round";
196  default: return "???";
197  }
198 }
199 
201 {
202  if ( str == "square" ) return Qt::SquareCap;
203  if ( str == "flat" ) return Qt::FlatCap;
204  if ( str == "round" ) return Qt::RoundCap;
205  return Qt::SquareCap;
206 }
207 
209 {
210  switch ( style )
211  {
212  case Qt::SquareCap: return "square";
213  case Qt::FlatCap: return "butt";
214  case Qt::RoundCap: return "round";
215  default: return "";
216  }
217 }
218 
220 {
221  if ( str == "square" ) return Qt::SquareCap;
222  if ( str == "butt" ) return Qt::FlatCap;
223  if ( str == "round" ) return Qt::RoundCap;
224  return Qt::SquareCap;
225 }
226 
228 {
229  switch ( style )
230  {
231  case Qt::SolidPattern : return "solid";
232  case Qt::HorPattern : return "horizontal";
233  case Qt::VerPattern : return "vertical";
234  case Qt::CrossPattern : return "cross";
235  case Qt::BDiagPattern : return "b_diagonal";
236  case Qt::FDiagPattern : return "f_diagonal";
237  case Qt::DiagCrossPattern : return "diagonal_x";
238  case Qt::Dense1Pattern : return "dense1";
239  case Qt::Dense2Pattern : return "dense2";
240  case Qt::Dense3Pattern : return "dense3";
241  case Qt::Dense4Pattern : return "dense4";
242  case Qt::Dense5Pattern : return "dense5";
243  case Qt::Dense6Pattern : return "dense6";
244  case Qt::Dense7Pattern : return "dense7";
245  case Qt::NoBrush : return "no";
246  default: return "???";
247  }
248 }
249 
251 {
252  if ( str == "solid" ) return Qt::SolidPattern;
253  if ( str == "horizontal" ) return Qt::HorPattern;
254  if ( str == "vertical" ) return Qt::VerPattern;
255  if ( str == "cross" ) return Qt::CrossPattern;
256  if ( str == "b_diagonal" ) return Qt::BDiagPattern;
257  if ( str == "f_diagonal" ) return Qt::FDiagPattern;
258  if ( str == "diagonal_x" ) return Qt::DiagCrossPattern;
259  if ( str == "dense1" ) return Qt::Dense1Pattern;
260  if ( str == "dense2" ) return Qt::Dense2Pattern;
261  if ( str == "dense3" ) return Qt::Dense3Pattern;
262  if ( str == "dense4" ) return Qt::Dense4Pattern;
263  if ( str == "dense5" ) return Qt::Dense5Pattern;
264  if ( str == "dense6" ) return Qt::Dense6Pattern;
265  if ( str == "dense7" ) return Qt::Dense7Pattern;
266  if ( str == "no" ) return Qt::NoBrush;
267  return Qt::SolidPattern;
268 }
269 
271 {
272  switch ( style )
273  {
274  case Qt::CrossPattern: return "cross";
275  case Qt::DiagCrossPattern: return "x";
276 
277  /* The following names are taken from the presentation "GeoServer
278  * Cartographic Rendering" by Andrea Aime at the FOSS4G 2010.
279  * (see http://2010.foss4g.org/presentations/3588.pdf)
280  */
281  case Qt::HorPattern: return "horline";
282  case Qt::VerPattern: return "line";
283  case Qt::BDiagPattern: return "slash";
284  case Qt::FDiagPattern: return "backslash";
285 
286  /* define the other names following the same pattern used above */
287  case Qt::Dense1Pattern:
288  case Qt::Dense2Pattern:
289  case Qt::Dense3Pattern:
290  case Qt::Dense4Pattern:
291  case Qt::Dense5Pattern:
292  case Qt::Dense6Pattern:
293  case Qt::Dense7Pattern:
294  return QString( "brush://%1" ).arg( encodeBrushStyle( style ) );
295 
296  default:
297  return QString();
298  }
299 }
300 
302 {
303  if ( str == "horline" ) return Qt::HorPattern;
304  if ( str == "line" ) return Qt::VerPattern;
305  if ( str == "cross" ) return Qt::CrossPattern;
306  if ( str == "slash" ) return Qt::BDiagPattern;
307  if ( str == "backshash" ) return Qt::FDiagPattern;
308  if ( str == "x" ) return Qt::DiagCrossPattern;
309 
310  if ( str.startsWith( "brush://" ) )
311  return decodeBrushStyle( str.mid( 8 ) );
312 
313  return Qt::NoBrush;
314 }
315 
317 {
318  return QString( "%1,%2" ).arg( point.x() ).arg( point.y() );
319 }
320 
322 {
323  QStringList lst = str.split( ',' );
324  if ( lst.count() != 2 )
325  return QPointF( 0, 0 );
326  return QPointF( lst[0].toDouble(), lst[1].toDouble() );
327 }
328 
330 {
331  return QString( "%1,%2,%3,%4,%5,%6" ).arg( mapUnitScale.minScale ).arg( mapUnitScale.maxScale )
332  .arg( mapUnitScale.minSizeMMEnabled ? 1 : 0 )
333  .arg( mapUnitScale.minSizeMM )
334  .arg( mapUnitScale.maxSizeMMEnabled ? 1 : 0 )
335  .arg( mapUnitScale.maxSizeMM );
336 }
337 
339 {
340  QStringList lst = str.split( ',' );
341  if ( lst.count() < 2 )
342  return QgsMapUnitScale();
343 
344  if ( lst.count() < 6 )
345  {
346  // old format
347  return QgsMapUnitScale( lst[0].toDouble(), lst[1].toDouble() );
348  }
349 
350  QgsMapUnitScale s( lst[0].toDouble(), lst[1].toDouble() );
351  s.minSizeMMEnabled = lst[2].toInt();
352  s.minSizeMM = lst[3].toDouble();
353  s.maxSizeMMEnabled = lst[4].toInt();
354  s.maxSizeMM = lst[5].toDouble();
355  return s;
356 }
357 
359 {
360  switch ( unit )
361  {
362  case QgsSymbolV2::MM:
363  return "MM";
365  return "MapUnit";
366  case QgsSymbolV2::Pixel:
367  return "Pixel";
368  default:
369  return "MM";
370  }
371 }
372 
374 {
375  if ( str == "MM" )
376  {
377  return QgsSymbolV2::MM;
378  }
379  else if ( str == "MapUnit" )
380  {
381  return QgsSymbolV2::MapUnit;
382  }
383  else if ( str == "Pixel" )
384  {
385  return QgsSymbolV2::Pixel;
386  }
387 
388  // millimeters are default
389  return QgsSymbolV2::MM;
390 }
391 
393 {
394  switch ( unit )
395  {
397  if ( scaleFactor )
398  *scaleFactor = 0.001; // from millimeters to meters
399  return "http://www.opengeospatial.org/se/units/metre";
400 
401  case QgsSymbolV2::MM:
402  default:
403  // pixel is the SLD default uom. The "standardized rendering pixel
404  // size" is defined to be 0.28mm × 0.28mm (millimeters).
405  if ( scaleFactor )
406  *scaleFactor = 0.28; // from millimeters to pixels
407 
408  // http://www.opengeospatial.org/sld/units/pixel
409  return QString();
410  }
411 }
412 
414 {
415  if ( str == "http://www.opengeospatial.org/se/units/metre" )
416  {
417  if ( scaleFactor )
418  *scaleFactor = 1000.0; // from meters to millimeters
419  return QgsSymbolV2::MapUnit;
420  }
421  else if ( str == "http://www.opengeospatial.org/se/units/foot" )
422  {
423  if ( scaleFactor )
424  *scaleFactor = 304.8; // from feet to meters
425  return QgsSymbolV2::MapUnit;
426  }
427 
428  // pixel is the SLD default uom. The "standardized rendering pixel
429  // size" is defined to be 0.28mm x 0.28mm (millimeters).
430  if ( scaleFactor )
431  *scaleFactor = 1 / 0.00028; // from pixels to millimeters
432  return QgsSymbolV2::MM;
433 }
434 
436 {
437  QString vectorString;
439  for ( ; it != v.constEnd(); ++it )
440  {
441  if ( it != v.constBegin() )
442  {
443  vectorString.append( ";" );
444  }
445  vectorString.append( QString::number( *it ) );
446  }
447  return vectorString;
448 }
449 
451 {
452  QVector<qreal> resultVector;
453 
454  QStringList realList = s.split( ";" );
455  QStringList::const_iterator it = realList.constBegin();
456  for ( ; it != realList.constEnd(); ++it )
457  {
458  resultVector.append( it->toDouble() );
459  }
460 
461  return resultVector;
462 }
463 
465 {
466  QString vectorString;
468  for ( ; it != v.constEnd(); ++it )
469  {
470  if ( it != v.constBegin() )
471  {
472  vectorString.append( " " );
473  }
474  vectorString.append( QString::number( *it ) );
475  }
476  return vectorString;
477 }
478 
480 {
481  QVector<qreal> resultVector;
482 
483  QStringList realList = s.split( " " );
484  QStringList::const_iterator it = realList.constBegin();
485  for ( ; it != realList.constEnd(); ++it )
486  {
487  resultVector.append( it->toDouble() );
488  }
489 
490  return resultVector;
491 }
492 
494 {
495  QString encodedValue;
496 
497  switch ( scaleMethod )
498  {
500  encodedValue = "diameter";
501  break;
503  encodedValue = "area";
504  break;
505  }
506  return encodedValue;
507 }
508 
510 {
511  QgsSymbolV2::ScaleMethod scaleMethod;
512 
513  if ( str == "diameter" )
514  {
515  scaleMethod = QgsSymbolV2::ScaleDiameter;
516  }
517  else
518  {
519  scaleMethod = QgsSymbolV2::ScaleArea;
520  }
521 
522  return scaleMethod;
523 }
524 
525 QPainter::CompositionMode QgsSymbolLayerV2Utils::decodeBlendMode( const QString &s )
526 {
527  if ( s.compare( "Lighten", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Lighten;
528  if ( s.compare( "Screen", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Screen;
529  if ( s.compare( "Dodge", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorDodge;
530  if ( s.compare( "Addition", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Plus;
531  if ( s.compare( "Darken", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Darken;
532  if ( s.compare( "Multiply", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Multiply;
533  if ( s.compare( "Burn", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorBurn;
534  if ( s.compare( "Overlay", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Overlay;
535  if ( s.compare( "SoftLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_SoftLight;
536  if ( s.compare( "HardLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_HardLight;
537  if ( s.compare( "Difference", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Difference;
538  if ( s.compare( "Subtract", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Exclusion;
539  return QPainter::CompositionMode_SourceOver; // "Normal"
540 }
541 
543 {
544  return QIcon( symbolPreviewPixmap( symbol, size ) );
545 }
546 
548 {
549  Q_ASSERT( symbol );
550 
551  QPixmap pixmap( size );
552  pixmap.fill( Qt::transparent );
553  QPainter painter;
554  painter.begin( &pixmap );
555  painter.setRenderHint( QPainter::Antialiasing );
556  if ( customContext )
557  customContext->setPainter( &painter );
558  symbol->drawPreviewIcon( &painter, size, customContext );
559  painter.end();
560  return pixmap;
561 }
562 
564 {
565  double maxBleed = 0;
566  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
567  {
568  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
569  double layerMaxBleed = layer->estimateMaxBleed();
570  maxBleed = layerMaxBleed > maxBleed ? layerMaxBleed : maxBleed;
571  }
572 
573  return maxBleed;
574 }
575 
577 {
578  QPicture picture;
579  QPainter painter;
580  painter.begin( &picture );
581  painter.setRenderHint( QPainter::Antialiasing );
582  QgsRenderContext renderContext = createRenderContext( &painter );
583  renderContext.setForceVectorOutput( true );
584  QgsSymbolV2RenderContext symbolContext( renderContext, units, 1.0, false, 0, 0, 0, scale );
585  layer->drawPreviewIcon( symbolContext, size );
586  painter.end();
587  return picture;
588 }
589 
591 {
592  QPixmap pixmap( size );
593  pixmap.fill( Qt::transparent );
594  QPainter painter;
595  painter.begin( &pixmap );
596  painter.setRenderHint( QPainter::Antialiasing );
597  QgsRenderContext renderContext = createRenderContext( &painter );
598  QgsSymbolV2RenderContext symbolContext( renderContext, u, 1.0, false, 0, 0, 0, scale );
599  layer->drawPreviewIcon( symbolContext, size );
600  painter.end();
601  return QIcon( pixmap );
602 }
603 
605 {
606  return QIcon( colorRampPreviewPixmap( ramp, size ) );
607 }
608 
610 {
611  QPixmap pixmap( size );
612  pixmap.fill( Qt::transparent );
613  // pixmap.fill( Qt::white ); // this makes the background white instead of transparent
614  QPainter painter;
615  painter.begin( &pixmap );
616 
617  //draw stippled background, for transparent images
618  drawStippledBackground( &painter, QRect( 0, 0, size.width(), size.height() ) );
619 
620  // antialising makes the colors duller, and no point in antialiasing a color ramp
621  // painter.setRenderHint( QPainter::Antialiasing );
622  for ( int i = 0; i < size.width(); i++ )
623  {
624  QPen pen( ramp->color(( double ) i / size.width() ) );
625  painter.setPen( pen );
626  painter.drawLine( i, 0, i, size.height() - 1 );
627  }
628  painter.end();
629  return pixmap;
630 }
631 
633 {
634  // create a 2x2 checker-board image
635  uchar pixDataRGB[] = { 255, 255, 255, 255,
636  127, 127, 127, 255,
637  127, 127, 127, 255,
638  255, 255, 255, 255
639  };
640  QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
641  // scale it to rect so at least 5 patterns are shown
642  int width = ( rect.width() < rect.height() ) ?
643  rect.width() / 2.5 : rect.height() / 2.5;
644  QPixmap pix = QPixmap::fromImage( img.scaled( width, width ) );
645  // fill rect with texture
646  QBrush brush;
647  brush.setTexture( pix );
648  painter->fillRect( rect, brush );
649 }
650 
651 #include <QPolygonF>
652 
653 #include <cmath>
654 #include <cfloat>
655 
656 
657 #if !defined(GEOS_VERSION_MAJOR) || !defined(GEOS_VERSION_MINOR) || \
658  ((GEOS_VERSION_MAJOR<3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR<3)))
659 // calculate line's angle and tangent
660 static bool lineInfo( QPointF p1, QPointF p2, double& angle, double& t )
661 {
662  double x1 = p1.x(), y1 = p1.y(), x2 = p2.x(), y2 = p2.y();
663 
664  if ( x1 == x2 && y1 == y2 )
665  return false;
666 
667  // tangent
668  t = ( x1 == x2 ? DBL_MAX : ( y2 - y1 ) / ( x2 - x1 ) );
669 
670  // angle
671  if ( t == DBL_MAX )
672  angle = ( y2 > y1 ? M_PI / 2 : M_PI * 3 / 2 ); // angle is 90 or 270
673  else if ( t == 0 )
674  angle = ( x2 > x1 ? 0 : M_PI ); // angle is 0 or 180
675  else if ( t >= 0 )
676  angle = ( y2 > y1 ? atan( t ) : M_PI + atan( t ) );
677  else // t < 0
678  angle = ( y2 > y1 ? M_PI + atan( t ) : atan( t ) );
679 
680  return true;
681 }
682 
683 // offset a point with an angle and distance
684 static QPointF offsetPoint( QPointF pt, double angle, double dist )
685 {
686  return QPointF( pt.x() + dist * cos( angle ), pt.y() + dist * sin( angle ) );
687 }
688 
689 // calc intersection of two (infinite) lines defined by one point and tangent
690 static QPointF linesIntersection( QPointF p1, double t1, QPointF p2, double t2 )
691 {
692  // parallel lines? (or the difference between angles is less than appr. 10 degree)
693  if (( t1 == DBL_MAX && t2 == DBL_MAX ) || qAbs( atan( t1 ) - atan( t2 ) ) < 0.175 )
694  return QPointF();
695 
696  double x, y;
697  if ( t1 == DBL_MAX || t2 == DBL_MAX )
698  {
699  // in case one line is with angle 90 resp. 270 degrees (tangent undefined)
700  // swap them so that line 2 is with undefined tangent
701  if ( t1 == DBL_MAX )
702  {
703  QPointF pSwp = p1; p1 = p2; p2 = pSwp;
704  double tSwp = t1; t1 = t2; t2 = tSwp;
705  }
706 
707  x = p2.x();
708  }
709  else
710  {
711  // usual case
712  x = (( p1.y() - p2.y() ) + t2 * p2.x() - t1 * p1.x() ) / ( t2 - t1 );
713  }
714 
715  y = p1.y() + t1 * ( x - p1.x() );
716  return QPointF( x, y );
717 }
718 #else
719 static QPolygonF makeOffsetGeometry( const QgsPolyline& polyline )
720 {
721  int i, pointCount = polyline.count();
722 
723  QPolygonF resultLine;
724  resultLine.resize( pointCount );
725 
726  const QgsPoint* tempPtr = polyline.data();
727 
728  for ( i = 0; i < pointCount; ++i, tempPtr++ )
729  resultLine[i] = QPointF( tempPtr->x(), tempPtr->y() );
730 
731  return resultLine;
732 }
733 static QList<QPolygonF> makeOffsetGeometry( const QgsPolygon& polygon )
734 {
735  QList<QPolygonF> resultGeom;
736  resultGeom.reserve( polygon.size() );
737  for ( int ring = 0; ring < polygon.size(); ++ring )
738  resultGeom.append( makeOffsetGeometry( polygon[ ring ] ) );
739  return resultGeom;
740 }
741 #endif
742 
743 QList<QPolygonF> offsetLine( QPolygonF polyline, double dist, QGis::GeometryType geometryType )
744 {
745  QList<QPolygonF> resultLine;
746 
747  if ( polyline.count() < 2 )
748  {
749  resultLine.append( polyline );
750  return resultLine;
751  }
752 
753  QPolygonF newLine;
754 
755  // need at least geos 3.3 for OffsetCurve tool
756 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
757  ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
758 
759  unsigned int i, pointCount = polyline.count();
760 
761  QgsPolyline tempPolyline( pointCount );
762  QPointF* tempPtr = polyline.data();
763  for ( i = 0; i < pointCount; ++i, tempPtr++ )
764  tempPolyline[i] = QgsPoint( tempPtr->rx(), tempPtr->ry() );
765 
766  QgsGeometry* tempGeometry = geometryType == QGis::Polygon ? QgsGeometry::fromPolygon( QgsPolygon() << tempPolyline ) : QgsGeometry::fromPolyline( tempPolyline );
767  if ( tempGeometry )
768  {
769  int quadSegments = 0; // we want mitre joins, not round joins
770  double mitreLimit = 2.0; // the default value in GEOS (5.0) allows for fairly sharp endings
771  QgsGeometry* offsetGeom = 0;
772  if ( geometryType == QGis::Polygon )
773  offsetGeom = tempGeometry->buffer( -dist, quadSegments, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, mitreLimit );
774  else
775  offsetGeom = tempGeometry->offsetCurve( dist, quadSegments, GEOSBUF_JOIN_MITRE, mitreLimit );
776 
777  if ( offsetGeom )
778  {
779  delete tempGeometry;
780  tempGeometry = offsetGeom;
781 
782  if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBLineString )
783  {
784  resultLine.append( makeOffsetGeometry( tempGeometry->asPolyline() ) );
785  delete tempGeometry;
786  return resultLine;
787  }
788  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBPolygon )
789  {
790  resultLine.append( makeOffsetGeometry( tempGeometry->asPolygon() ) );
791  delete tempGeometry;
792  return resultLine;
793  }
794  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBMultiLineString )
795  {
796  QgsMultiPolyline tempMPolyline = tempGeometry->asMultiPolyline();
797  resultLine.reserve( tempMPolyline.count() );
798  for ( int part = 0; part < tempMPolyline.count(); ++part )
799  {
800  resultLine.append( makeOffsetGeometry( tempMPolyline[ part ] ) );
801  }
802  delete tempGeometry;
803  return resultLine;
804  }
805  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBMultiPolygon )
806  {
807  QgsMultiPolygon tempMPolygon = tempGeometry->asMultiPolygon();
808  resultLine.reserve( tempMPolygon.count() );
809  for ( int part = 0; part < tempMPolygon.count(); ++part )
810  {
811  resultLine.append( makeOffsetGeometry( tempMPolygon[ part ] ) );
812  }
813  delete tempGeometry;
814  return resultLine;
815  }
816  }
817  delete tempGeometry;
818  }
819 
820  // returns original polyline when 'GEOSOffsetCurve' fails!
821  resultLine.append( polyline );
822  return resultLine;
823 
824 #else
825 
826  double angle = 0.0, t_new, t_old = 0;
827  QPointF pt_old, pt_new;
828  QPointF p1 = polyline[0], p2;
829  bool first_point = true;
830 
831  for ( int i = 1; i < polyline.count(); i++ )
832  {
833  p2 = polyline[i];
834 
835  if ( !lineInfo( p1, p2, angle, t_new ) )
836  continue; // not a line...
837 
838  pt_new = offsetPoint( p1, angle + M_PI / 2, dist );
839 
840  if ( ! first_point )
841  {
842  // if it's not the first line segment
843  // calc intersection with last line (with offset)
844  QPointF pt_tmp = linesIntersection( pt_old, t_old, pt_new, t_new );
845  if ( !pt_tmp.isNull() )
846  pt_new = pt_tmp;
847  }
848 
849  newLine.append( pt_new );
850 
851  pt_old = pt_new;
852  t_old = t_new;
853  p1 = p2;
854  first_point = false;
855  }
856 
857  // last line segment:
858  pt_new = offsetPoint( p2, angle + M_PI / 2, dist );
859  newLine.append( pt_new );
860 
861  resultLine.append( newLine );
862  return resultLine;
863 
864 #endif
865 }
866 
867 QList<QPolygonF> offsetLine( const QPolygonF& polyline, double dist )
868 {
869  QGis::GeometryType geometryType = QGis::Point;
870  int pointCount = polyline.count();
871 
872  if ( pointCount > 3 && polyline[ 0 ].x() == polyline[ pointCount - 1 ].x() && polyline[ 0 ].y() == polyline[ pointCount - 1 ].y() )
873  {
874  geometryType = QGis::Polygon;
875  }
876  else if ( pointCount > 1 )
877  {
878  geometryType = QGis::Line;
879  }
880  return offsetLine( polyline, dist, geometryType );
881 }
882 
884 
885 
887 {
888  QgsSymbolLayerV2List layers;
889  QDomNode layerNode = element.firstChild();
890 
891  while ( !layerNode.isNull() )
892  {
893  QDomElement e = layerNode.toElement();
894  if ( !e.isNull() )
895  {
896  if ( e.tagName() != "layer" )
897  {
898  QgsDebugMsg( "unknown tag " + e.tagName() );
899  }
900  else
901  {
902  QgsSymbolLayerV2* layer = loadSymbolLayer( e );
903 
904  if ( layer != NULL )
905  {
906  // Dealing with sub-symbols nested into a layer
907  QDomElement s = e.firstChildElement( "symbol" );
908  if ( !s.isNull() )
909  {
910  QgsSymbolV2* subSymbol = loadSymbol( s );
911  bool res = layer->setSubSymbol( subSymbol );
912  if ( !res )
913  {
914  QgsDebugMsg( "symbol layer refused subsymbol: " + s.attribute( "name" ) );
915  }
916  }
917  layers.append( layer );
918  }
919  }
920  }
921  layerNode = layerNode.nextSibling();
922  }
923 
924  if ( layers.count() == 0 )
925  {
926  QgsDebugMsg( "no layers for symbol" );
927  return NULL;
928  }
929 
930  QString symbolType = element.attribute( "type" );
931 
932  QgsSymbolV2* symbol = 0;
933  if ( symbolType == "line" )
934  symbol = new QgsLineSymbolV2( layers );
935  else if ( symbolType == "fill" )
936  symbol = new QgsFillSymbolV2( layers );
937  else if ( symbolType == "marker" )
938  symbol = new QgsMarkerSymbolV2( layers );
939  else
940  {
941  QgsDebugMsg( "unknown symbol type " + symbolType );
942  return NULL;
943  }
944 
945  if ( element.hasAttribute( "outputUnit" ) )
946  {
947  symbol->setOutputUnit( decodeOutputUnit( element.attribute( "outputUnit" ) ) );
948  }
949  if ( element.hasAttribute(( "mapUnitScale" ) ) )
950  {
951  QgsMapUnitScale mapUnitScale;
952  mapUnitScale.minScale = element.attribute( "mapUnitMinScale", "0.0" ).toDouble();
953  mapUnitScale.maxScale = element.attribute( "mapUnitMaxScale", "0.0" ).toDouble();
954  symbol->setMapUnitScale( mapUnitScale );
955  }
956  symbol->setAlpha( element.attribute( "alpha", "1.0" ).toDouble() );
957  symbol->setClipFeaturesToExtent( element.attribute( "clip_to_extent", "1" ).toInt() );
958 
959  return symbol;
960 }
961 
963 {
964  QString layerClass = element.attribute( "class" );
965  bool locked = element.attribute( "locked" ).toInt();
966  int pass = element.attribute( "pass" ).toInt();
967 
968  // parse properties
969  QgsStringMap props = parseProperties( element );
970 
971  QgsSymbolLayerV2* layer;
972  layer = QgsSymbolLayerV2Registry::instance()->createSymbolLayer( layerClass, props );
973  if ( layer )
974  {
975  layer->setLocked( locked );
976  layer->setRenderingPass( pass );
977 
978  //restore layer effect
979  QDomElement effectElem = element.firstChildElement( "effect" );
980  if ( !effectElem.isNull() )
981  {
982  layer->setPaintEffect( QgsPaintEffectRegistry::instance()->createEffect( effectElem ) );
983  }
984  return layer;
985  }
986  else
987  {
988  QgsDebugMsg( "unknown class " + layerClass );
989  return NULL;
990  }
991 }
992 
994 {
995  switch ( type )
996  {
997  case QgsSymbolV2::Line: return "line";
998  case QgsSymbolV2::Marker: return "marker";
999  case QgsSymbolV2::Fill: return "fill";
1000  default: return "";
1001  }
1002 }
1003 
1005 {
1006  Q_ASSERT( symbol );
1007  QDomElement symEl = doc.createElement( "symbol" );
1008  symEl.setAttribute( "type", _nameForSymbolType( symbol->type() ) );
1009  symEl.setAttribute( "name", name );
1010  symEl.setAttribute( "alpha", QString::number( symbol->alpha() ) );
1011  symEl.setAttribute( "clip_to_extent", symbol->clipFeaturesToExtent() ? "1" : "0" );
1012  QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) );
1013 
1014  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
1015  {
1016  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
1017 
1018  QDomElement layerEl = doc.createElement( "layer" );
1019  layerEl.setAttribute( "class", layer->layerType() );
1020  layerEl.setAttribute( "locked", layer->isLocked() );
1021  layerEl.setAttribute( "pass", layer->renderingPass() );
1022  saveProperties( layer->properties(), doc, layerEl );
1024  layer->paintEffect()->saveProperties( doc, layerEl );
1025 
1026  if ( layer->subSymbol() != NULL )
1027  {
1028  QString subname = QString( "@%[email protected]%2" ).arg( name ).arg( i );
1029  QDomElement subEl = saveSymbol( subname, layer->subSymbol(), doc );
1030  layerEl.appendChild( subEl );
1031  }
1032  symEl.appendChild( layerEl );
1033  }
1034 
1035  return symEl;
1036 }
1037 
1039 {
1040  QDomDocument doc( "qgis-symbol-definition" );
1041  QDomElement symbolElem = saveSymbol( "symbol", symbol, doc );
1042  QString props;
1043  QTextStream stream( &props );
1044  symbolElem.save( stream, -1 );
1045  return props;
1046 }
1047 
1049  QGis::GeometryType geomType,
1050  QgsSymbolLayerV2List &layers )
1051 {
1052  QgsDebugMsg( "Entered." );
1053 
1054  if ( element.isNull() )
1055  return false;
1056 
1057  QgsSymbolLayerV2 *l = 0;
1058 
1059  QString symbolizerName = element.localName();
1060 
1061  if ( symbolizerName == "PointSymbolizer" )
1062  {
1063  // first check for Graphic element, nothing will be rendered if not found
1064  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1065  if ( graphicElem.isNull() )
1066  {
1067  QgsDebugMsg( "Graphic element not found in PointSymbolizer" );
1068  }
1069  else
1070  {
1071  switch ( geomType )
1072  {
1073  case QGis::Polygon:
1074  // polygon layer and point symbolizer: draw poligon centroid
1075  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "CentroidFill", element );
1076  if ( l )
1077  layers.append( l );
1078 
1079  break;
1080 
1081  case QGis::Point:
1082  // point layer and point symbolizer: use markers
1083  l = createMarkerLayerFromSld( element );
1084  if ( l )
1085  layers.append( l );
1086 
1087  break;
1088 
1089  case QGis::Line:
1090  // line layer and point symbolizer: draw central point
1091  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
1092  if ( l )
1093  layers.append( l );
1094 
1095  break;
1096 
1097  default:
1098  break;
1099  }
1100  }
1101  }
1102 
1103  if ( symbolizerName == "LineSymbolizer" )
1104  {
1105  // check for Stroke element, nothing will be rendered if not found
1106  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1107  if ( strokeElem.isNull() )
1108  {
1109  QgsDebugMsg( "Stroke element not found in LineSymbolizer" );
1110  }
1111  else
1112  {
1113  switch ( geomType )
1114  {
1115  case QGis::Polygon:
1116  case QGis::Line:
1117  // polygon layer and line symbolizer: draw polygon outline
1118  // line layer and line symbolizer: draw line
1119  l = createLineLayerFromSld( element );
1120  if ( l )
1121  layers.append( l );
1122 
1123  break;
1124 
1125  case QGis::Point:
1126  // point layer and line symbolizer: draw a little line marker
1127  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
1128  if ( l )
1129  layers.append( l );
1130 
1131  default:
1132  break;
1133  }
1134  }
1135  }
1136 
1137  if ( symbolizerName == "PolygonSymbolizer" )
1138  {
1139  // get Fill and Stroke elements, nothing will be rendered if both are missing
1140  QDomElement fillElem = element.firstChildElement( "Fill" );
1141  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1142  if ( fillElem.isNull() && strokeElem.isNull() )
1143  {
1144  QgsDebugMsg( "neither Fill nor Stroke element not found in PolygonSymbolizer" );
1145  }
1146  else
1147  {
1148  QgsSymbolLayerV2 *l = 0;
1149 
1150  switch ( geomType )
1151  {
1152  case QGis::Polygon:
1153  // polygon layer and polygon symbolizer: draw fill
1154 
1155  l = createFillLayerFromSld( element );
1156  if ( l )
1157  {
1158  layers.append( l );
1159 
1160  // SVGFill and SimpleFill symbolLayerV2 supports outline internally,
1161  // so don't go forward to create a different symbolLayerV2 for outline
1162  if ( l->layerType() == "SimpleFill" || l->layerType() == "SVGFill" )
1163  break;
1164  }
1165 
1166  // now create polygon outline
1167  // polygon layer and polygon symbolizer: draw polygon outline
1168  l = createLineLayerFromSld( element );
1169  if ( l )
1170  layers.append( l );
1171 
1172  break;
1173 
1174  case QGis::Line:
1175  // line layer and polygon symbolizer: draw line
1176  l = createLineLayerFromSld( element );
1177  if ( l )
1178  layers.append( l );
1179 
1180  break;
1181 
1182  case QGis::Point:
1183  // point layer and polygon symbolizer: draw a square marker
1184  convertPolygonSymbolizerToPointMarker( element, layers );
1185  break;
1186 
1187  default:
1188  break;
1189  }
1190  }
1191  }
1192 
1193  return true;
1194 }
1195 
1197 {
1198  QDomElement fillElem = element.firstChildElement( "Fill" );
1199  if ( fillElem.isNull() )
1200  {
1201  QgsDebugMsg( "Fill element not found" );
1202  return NULL;
1203  }
1204 
1205  QgsSymbolLayerV2 *l = 0;
1206 
1207  if ( needLinePatternFill( element ) )
1208  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "LinePatternFill", element );
1209  else if ( needPointPatternFill( element ) )
1210  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "PointPatternFill", element );
1211  else if ( needSvgFill( element ) )
1212  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SVGFill", element );
1213  else
1214  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleFill", element );
1215 
1216  return l;
1217 }
1218 
1220 {
1221  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1222  if ( strokeElem.isNull() )
1223  {
1224  QgsDebugMsg( "Stroke element not found" );
1225  return NULL;
1226  }
1227 
1228  QgsSymbolLayerV2 *l = 0;
1229 
1230  if ( needMarkerLine( element ) )
1231  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
1232  else
1233  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleLine", element );
1234 
1235  return l;
1236 }
1237 
1239 {
1240  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1241  if ( graphicElem.isNull() )
1242  {
1243  QgsDebugMsg( "Graphic element not found" );
1244  return NULL;
1245  }
1246 
1247  QgsSymbolLayerV2 *l = 0;
1248 
1249  if ( needFontMarker( element ) )
1250  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "FontMarker", element );
1251  else if ( needSvgMarker( element ) )
1252  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SvgMarker", element );
1253  else if ( needEllipseMarker( element ) )
1254  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "EllipseMarker", element );
1255  else
1256  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
1257 
1258  return l;
1259 }
1260 
1262 {
1263  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1264  if ( graphicElem.isNull() )
1265  return false;
1266 
1267  QDomElement externalGraphicElem = graphicElem.firstChildElement( "ExternalGraphic" );
1268  if ( externalGraphicElem.isNull() )
1269  return false;
1270 
1271  // check for format
1272  QDomElement formatElem = externalGraphicElem.firstChildElement( "Format" );
1273  if ( formatElem.isNull() )
1274  return false;
1275 
1276  QString format = formatElem.firstChild().nodeValue();
1277  if ( format != "image/svg+xml" )
1278  {
1279  QgsDebugMsg( "unsupported External Graphic format found: " + format );
1280  return false;
1281  }
1282 
1283  // check for a valid content
1284  QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( "OnlineResource" );
1285  QDomElement inlineContentElem = externalGraphicElem.firstChildElement( "InlineContent" );
1286  if ( !onlineResourceElem.isNull() )
1287  {
1288  return true;
1289  }
1290 #if 0
1291  else if ( !inlineContentElem.isNull() )
1292  {
1293  return false; // not implemented yet
1294  }
1295 #endif
1296  else
1297  {
1298  return false;
1299  }
1300 }
1301 
1303 {
1304  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1305  if ( graphicElem.isNull() )
1306  return false;
1307 
1308  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1309  if ( markElem.isNull() )
1310  return false;
1311 
1312  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
1313  if ( wellKnownNameElem.isNull() )
1314  return false;
1315 
1316  return true;
1317 }
1318 
1319 
1321 {
1322  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1323  if ( graphicElem.isNull() )
1324  return false;
1325 
1326  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1327  if ( markElem.isNull() )
1328  return false;
1329 
1330  // check for format
1331  QDomElement formatElem = markElem.firstChildElement( "Format" );
1332  if ( formatElem.isNull() )
1333  return false;
1334 
1335  QString format = formatElem.firstChild().nodeValue();
1336  if ( format != "ttf" )
1337  {
1338  QgsDebugMsg( "unsupported Graphic Mark format found: " + format );
1339  return false;
1340  }
1341 
1342  // check for a valid content
1343  QDomElement onlineResourceElem = markElem.firstChildElement( "OnlineResource" );
1344  QDomElement inlineContentElem = markElem.firstChildElement( "InlineContent" );
1345  if ( !onlineResourceElem.isNull() )
1346  {
1347  // mark with ttf format has a markIndex element
1348  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
1349  if ( !markIndexElem.isNull() )
1350  return true;
1351  }
1352  else if ( !inlineContentElem.isNull() )
1353  {
1354  return false; // not implemented yet
1355  }
1356 
1357  return false;
1358 }
1359 
1361 {
1362  return hasExternalGraphic( element );
1363 }
1364 
1366 {
1367  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1368  if ( graphicElem.isNull() )
1369  return false;
1370 
1371  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
1372  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1373  {
1374  if ( it.key() == "widthHeightFactor" )
1375  {
1376  return true;
1377  }
1378  }
1379 
1380  return false;
1381 }
1382 
1384 {
1385  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1386  if ( strokeElem.isNull() )
1387  return false;
1388 
1389  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1390  if ( graphicStrokeElem.isNull() )
1391  return false;
1392 
1393  return hasWellKnownMark( graphicStrokeElem );
1394 }
1395 
1397 {
1398  QDomElement fillElem = element.firstChildElement( "Fill" );
1399  if ( fillElem.isNull() )
1400  return false;
1401 
1402  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1403  if ( graphicFillElem.isNull() )
1404  return false;
1405 
1406  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1407  if ( graphicElem.isNull() )
1408  return false;
1409 
1410  // line pattern fill uses horline wellknown marker with an angle
1411 
1412  QString name;
1413  QColor fillColor, borderColor;
1414  double size, borderWidth;
1415  Qt::PenStyle borderStyle;
1416  if ( !wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderStyle, borderWidth, size ) )
1417  return false;
1418 
1419  if ( name != "horline" )
1420  return false;
1421 
1422  QString angleFunc;
1423  if ( !rotationFromSldElement( graphicElem, angleFunc ) )
1424  return false;
1425 
1426  bool ok;
1427  double angle = angleFunc.toDouble( &ok );
1428  if ( !ok || angle == 0 )
1429  return false;
1430 
1431  return true;
1432 }
1433 
1435 {
1436  Q_UNUSED( element );
1437  return false;
1438 }
1439 
1441 {
1442  QDomElement fillElem = element.firstChildElement( "Fill" );
1443  if ( fillElem.isNull() )
1444  return false;
1445 
1446  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1447  if ( graphicFillElem.isNull() )
1448  return false;
1449 
1450  return hasExternalGraphic( graphicFillElem );
1451 }
1452 
1453 
1455 {
1456  QgsDebugMsg( "Entered." );
1457 
1458  /* SE 1.1 says about PolygonSymbolizer:
1459  if a point geometry is referenced instead of a polygon,
1460  then a small, square, ortho-normal polygon should be
1461  constructed for rendering.
1462  */
1463 
1464  QgsSymbolLayerV2List layers;
1465 
1466  // retrieve both Fill and Stroke elements
1467  QDomElement fillElem = element.firstChildElement( "Fill" );
1468  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1469 
1470  // first symbol layer
1471  {
1472  bool validFill = false, validBorder = false;
1473 
1474  // check for simple fill
1475  // Fill element can contain some SvgParameter elements
1476  QColor fillColor;
1477  Qt::BrushStyle fillStyle;
1478 
1479  if ( fillFromSld( fillElem, fillStyle, fillColor ) )
1480  validFill = true;
1481 
1482  // check for simple outline
1483  // Stroke element can contain some SvgParameter elements
1484  QColor borderColor;
1485  Qt::PenStyle borderStyle;
1486  double borderWidth = 1.0, dashOffset = 0.0;
1487  QVector<qreal> customDashPattern;
1488 
1489  if ( lineFromSld( strokeElem, borderStyle, borderColor, borderWidth,
1490  0, 0, &customDashPattern, &dashOffset ) )
1491  validBorder = true;
1492 
1493  if ( validFill || validBorder )
1494  {
1495  QgsStringMap map;
1496  map["name"] = "square";
1497  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1498  map["color_border"] = encodeColor( validBorder ? borderColor : Qt::transparent );
1499  map["size"] = QString::number( 6 );
1500  map["angle"] = QString::number( 0 );
1501  map["offset"] = encodePoint( QPointF( 0, 0 ) );
1502  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SimpleMarker", map ) );
1503  }
1504  }
1505 
1506  // second symbol layer
1507  {
1508  bool validFill = false, validBorder = false;
1509 
1510  // check for graphic fill
1511  QString name, format;
1512  int markIndex = -1;
1513  QColor fillColor, borderColor;
1514  double borderWidth = 1.0, size = 0.0, angle = 0.0;
1515  QPointF anchor, offset;
1516 
1517  // Fill element can contain a GraphicFill element
1518  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1519  if ( !graphicFillElem.isNull() )
1520  {
1521  // GraphicFill element must contain a Graphic element
1522  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1523  if ( !graphicElem.isNull() )
1524  {
1525  // Graphic element can contains some ExternalGraphic and Mark element
1526  // search for the first supported one and use it
1527  bool found = false;
1528 
1529  QDomElement graphicChildElem = graphicElem.firstChildElement();
1530  while ( !graphicChildElem.isNull() )
1531  {
1532  if ( graphicChildElem.localName() == "Mark" )
1533  {
1534  // check for a well known name
1535  QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( "WellKnownName" );
1536  if ( !wellKnownNameElem.isNull() )
1537  {
1538  name = wellKnownNameElem.firstChild().nodeValue();
1539  found = true;
1540  break;
1541  }
1542  }
1543 
1544  if ( graphicChildElem.localName() == "ExternalGraphic" || graphicChildElem.localName() == "Mark" )
1545  {
1546  // check for external graphic format
1547  QDomElement formatElem = graphicChildElem.firstChildElement( "Format" );
1548  if ( formatElem.isNull() )
1549  continue;
1550 
1551  format = formatElem.firstChild().nodeValue();
1552 
1553  // TODO: remove this check when more formats will be supported
1554  // only SVG external graphics are supported in this moment
1555  if ( graphicChildElem.localName() == "ExternalGraphic" && format != "image/svg+xml" )
1556  continue;
1557 
1558  // TODO: remove this check when more formats will be supported
1559  // only ttf marks are supported in this moment
1560  if ( graphicChildElem.localName() == "Mark" && format != "ttf" )
1561  continue;
1562 
1563  // check for a valid content
1564  QDomElement onlineResourceElem = graphicChildElem.firstChildElement( "OnlineResource" );
1565  QDomElement inlineContentElem = graphicChildElem.firstChildElement( "InlineContent" );
1566 
1567  if ( !onlineResourceElem.isNull() )
1568  {
1569  name = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
1570 
1571  if ( graphicChildElem.localName() == "Mark" && format == "ttf" )
1572  {
1573  // mark with ttf format may have a name like ttf://fontFamily
1574  if ( name.startsWith( "ttf://" ) )
1575  name = name.mid( 6 );
1576 
1577  // mark with ttf format has a markIndex element
1578  QDomElement markIndexElem = graphicChildElem.firstChildElement( "MarkIndex" );
1579  if ( markIndexElem.isNull() )
1580  continue;
1581 
1582  bool ok;
1583  int v = markIndexElem.firstChild().nodeValue().toInt( &ok );
1584  if ( !ok || v < 0 )
1585  continue;
1586 
1587  markIndex = v;
1588  }
1589 
1590  found = true;
1591  break;
1592  }
1593 #if 0
1594  else if ( !inlineContentElem.isNull() )
1595  continue; // TODO: not implemented yet
1596 #endif
1597  else
1598  continue;
1599  }
1600 
1601  // if Mark element is present but it doesn't contains neither
1602  // WellKnownName nor OnlineResource nor InlineContent,
1603  // use the default mark (square)
1604  if ( graphicChildElem.localName() == "Mark" )
1605  {
1606  name = "square";
1607  found = true;
1608  break;
1609  }
1610  }
1611 
1612  // if found a valid Mark, check for its Fill and Stroke element
1613  if ( found && graphicChildElem.localName() == "Mark" )
1614  {
1615  // XXX: recursive definition!?! couldn't be dangerous???
1616  // to avoid recursion we handle only simple fill and simple stroke
1617 
1618  // check for simple fill
1619  // Fill element can contain some SvgParameter elements
1620  Qt::BrushStyle markFillStyle;
1621 
1622  QDomElement markFillElem = graphicChildElem.firstChildElement( "Fill" );
1623  if ( fillFromSld( markFillElem, markFillStyle, fillColor ) )
1624  validFill = true;
1625 
1626  // check for simple outline
1627  // Stroke element can contain some SvgParameter elements
1628  Qt::PenStyle borderStyle;
1629  double borderWidth = 1.0, dashOffset = 0.0;
1630  QVector<qreal> customDashPattern;
1631 
1632  QDomElement markStrokeElem = graphicChildElem.firstChildElement( "Stroke" );
1633  if ( lineFromSld( markStrokeElem, borderStyle, borderColor, borderWidth,
1634  0, 0, &customDashPattern, &dashOffset ) )
1635  validBorder = true;
1636  }
1637 
1638  if ( found )
1639  {
1640  // check for Opacity, Size, Rotation, AnchorPoint, Displacement
1641  QDomElement opacityElem = graphicElem.firstChildElement( "Opacity" );
1642  if ( !opacityElem.isNull() )
1643  fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) );
1644 
1645  QDomElement sizeElem = graphicElem.firstChildElement( "Size" );
1646  if ( !sizeElem.isNull() )
1647  {
1648  bool ok;
1649  double v = sizeElem.firstChild().nodeValue().toDouble( &ok );
1650  if ( ok && v > 0 )
1651  size = v;
1652  }
1653 
1654  QString angleFunc;
1655  if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() )
1656  {
1657  bool ok;
1658  double v = angleFunc.toDouble( &ok );
1659  if ( ok )
1660  angle = v;
1661  }
1662 
1663  displacementFromSldElement( graphicElem, offset );
1664  }
1665  }
1666  }
1667 
1668  if ( validFill || validBorder )
1669  {
1670  if ( format == "image/svg+xml" )
1671  {
1672  QgsStringMap map;
1673  map["name"] = name;
1674  map["fill"] = fillColor.name();
1675  map["outline"] = borderColor.name();
1676  map["outline-width"] = QString::number( borderWidth );
1677  if ( !qgsDoubleNear( size, 0.0 ) )
1678  map["size"] = QString::number( size );
1679  if ( !qgsDoubleNear( angle, 0.0 ) )
1680  map["angle"] = QString::number( angle );
1681  if ( !offset.isNull() )
1682  map["offset"] = encodePoint( offset );
1683  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SvgMarker", map ) );
1684  }
1685  else if ( format == "ttf" )
1686  {
1687  QgsStringMap map;
1688  map["font"] = name;
1689  map["chr"] = markIndex;
1690  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1691  if ( size > 0 )
1692  map["size"] = QString::number( size );
1693  if ( !qgsDoubleNear( angle, 0.0 ) )
1694  map["angle"] = QString::number( angle );
1695  if ( !offset.isNull() )
1696  map["offset"] = encodePoint( offset );
1697  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "FontMarker", map ) );
1698  }
1699  }
1700  }
1701 
1702  if ( layers.isEmpty() )
1703  return false;
1704 
1705  layerList << layers;
1706  layers.clear();
1707  return true;
1708 }
1709 
1710 void QgsSymbolLayerV2Utils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor& color )
1711 {
1712  QString patternName;
1713  switch ( brushStyle )
1714  {
1715  case Qt::NoBrush:
1716  return;
1717 
1718  case Qt::SolidPattern:
1719  if ( color.isValid() )
1720  {
1721  element.appendChild( createSvgParameterElement( doc, "fill", color.name() ) );
1722  if ( color.alpha() < 255 )
1723  element.appendChild( createSvgParameterElement( doc, "fill-opacity", encodeSldAlpha( color.alpha() ) ) );
1724  }
1725  return;
1726 
1727  case Qt::CrossPattern:
1728  case Qt::DiagCrossPattern:
1729  case Qt::HorPattern:
1730  case Qt::VerPattern:
1731  case Qt::BDiagPattern:
1732  case Qt::FDiagPattern:
1733  case Qt::Dense1Pattern:
1734  case Qt::Dense2Pattern:
1735  case Qt::Dense3Pattern:
1736  case Qt::Dense4Pattern:
1737  case Qt::Dense5Pattern:
1738  case Qt::Dense6Pattern:
1739  case Qt::Dense7Pattern:
1740  patternName = encodeSldBrushStyle( brushStyle );
1741  break;
1742 
1743  default:
1744  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) );
1745  return;
1746  }
1747 
1748  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1749  element.appendChild( graphicFillElem );
1750 
1751  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1752  graphicFillElem.appendChild( graphicElem );
1753 
1754  QColor fillColor = patternName.startsWith( "brush://" ) ? color : QColor();
1755  QColor borderColor = !patternName.startsWith( "brush://" ) ? color : QColor();
1756 
1757  /* Use WellKnownName tag to handle QT brush styles. */
1758  wellKnownMarkerToSld( doc, graphicElem, patternName, fillColor, borderColor, Qt::SolidLine, -1, -1 );
1759 }
1760 
1761 bool QgsSymbolLayerV2Utils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color )
1762 {
1763  QgsDebugMsg( "Entered." );
1764 
1765  brushStyle = Qt::SolidPattern;
1766  color = QColor( "#808080" );
1767 
1768  if ( element.isNull() )
1769  {
1770  brushStyle = Qt::NoBrush;
1771  color = QColor();
1772  return true;
1773  }
1774 
1775  QDomElement graphicFillElem = element.firstChildElement( "GraphicFill" );
1776  // if no GraphicFill element is found, it's a solid fill
1777  if ( graphicFillElem.isNull() )
1778  {
1779  QgsStringMap svgParams = getSvgParameterList( element );
1780  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1781  {
1782  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
1783 
1784  if ( it.key() == "fill" )
1785  color = QColor( it.value() );
1786  else if ( it.key() == "fill-opacity" )
1787  color.setAlpha( decodeSldAlpha( it.value() ) );
1788  }
1789  }
1790  else // wellKnown marker
1791  {
1792  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1793  if ( graphicElem.isNull() )
1794  return false; // Graphic is required within GraphicFill
1795 
1796  QString patternName = "square";
1797  QColor fillColor, borderColor;
1798  double borderWidth, size;
1799  Qt::PenStyle borderStyle;
1800  if ( !wellKnownMarkerFromSld( graphicElem, patternName, fillColor, borderColor, borderStyle, borderWidth, size ) )
1801  return false;
1802 
1803  brushStyle = decodeSldBrushStyle( patternName );
1804  if ( brushStyle == Qt::NoBrush )
1805  return false; // unable to decode brush style
1806 
1807  QColor c = patternName.startsWith( "brush://" ) ? fillColor : borderColor;
1808  if ( c.isValid() )
1809  color = c;
1810  }
1811 
1812  return true;
1813 }
1814 
1816  Qt::PenStyle penStyle, const QColor& color, double width,
1817  const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
1818  const QVector<qreal> *customDashPattern, double dashOffset )
1819 {
1820  QVector<qreal> dashPattern;
1821  const QVector<qreal> *pattern = &dashPattern;
1822 
1823  if ( penStyle == Qt::CustomDashLine && !customDashPattern )
1824  {
1825  element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) );
1826  penStyle = Qt::DashLine;
1827  }
1828 
1829  switch ( penStyle )
1830  {
1831  case Qt::NoPen:
1832  return;
1833 
1834  case Qt::SolidLine:
1835  break;
1836 
1837  case Qt::DashLine:
1838  dashPattern.push_back( 4.0 );
1839  dashPattern.push_back( 2.0 );
1840  break;
1841  case Qt::DotLine:
1842  dashPattern.push_back( 1.0 );
1843  dashPattern.push_back( 2.0 );
1844  break;
1845  case Qt::DashDotLine:
1846  dashPattern.push_back( 4.0 );
1847  dashPattern.push_back( 2.0 );
1848  dashPattern.push_back( 1.0 );
1849  dashPattern.push_back( 2.0 );
1850  break;
1851  case Qt::DashDotDotLine:
1852  dashPattern.push_back( 4.0 );
1853  dashPattern.push_back( 2.0 );
1854  dashPattern.push_back( 1.0 );
1855  dashPattern.push_back( 2.0 );
1856  dashPattern.push_back( 1.0 );
1857  dashPattern.push_back( 2.0 );
1858  break;
1859 
1860  case Qt::CustomDashLine:
1861  Q_ASSERT( customDashPattern );
1862  pattern = customDashPattern;
1863  break;
1864 
1865  default:
1866  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
1867  return;
1868  }
1869 
1870  if ( color.isValid() )
1871  {
1872  element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) );
1873  if ( color.alpha() < 255 )
1874  element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) );
1875  }
1876  if ( width > 0 )
1877  element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) );
1878  if ( penJoinStyle )
1879  element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) );
1880  if ( penCapStyle )
1881  element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) );
1882 
1883  if ( pattern->size() > 0 )
1884  {
1885  element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *pattern ) ) );
1886  if ( !qgsDoubleNear( dashOffset, 0.0 ) )
1887  element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) );
1888  }
1889 }
1890 
1891 
1893  Qt::PenStyle &penStyle, QColor &color, double &width,
1894  Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle,
1895  QVector<qreal> *customDashPattern, double *dashOffset )
1896 {
1897  QgsDebugMsg( "Entered." );
1898 
1899  penStyle = Qt::SolidLine;
1900  color = QColor( "#000000" );
1901  width = 1;
1902  if ( penJoinStyle )
1903  *penJoinStyle = Qt::BevelJoin;
1904  if ( penCapStyle )
1905  *penCapStyle = Qt::SquareCap;
1906  if ( customDashPattern )
1907  customDashPattern->clear();
1908  if ( dashOffset )
1909  *dashOffset = 0;
1910 
1911  if ( element.isNull() )
1912  {
1913  penStyle = Qt::NoPen;
1914  color = QColor();
1915  return true;
1916  }
1917 
1918  QgsStringMap svgParams = getSvgParameterList( element );
1919  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1920  {
1921  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
1922 
1923  if ( it.key() == "stroke" )
1924  {
1925  color = QColor( it.value() );
1926  }
1927  else if ( it.key() == "stroke-opacity" )
1928  {
1929  color.setAlpha( decodeSldAlpha( it.value() ) );
1930  }
1931  else if ( it.key() == "stroke-width" )
1932  {
1933  bool ok;
1934  double w = it.value().toDouble( &ok );
1935  if ( ok )
1936  width = w;
1937  }
1938  else if ( it.key() == "stroke-linejoin" && penJoinStyle )
1939  {
1940  *penJoinStyle = decodeSldLineJoinStyle( it.value() );
1941  }
1942  else if ( it.key() == "stroke-linecap" && penCapStyle )
1943  {
1944  *penCapStyle = decodeSldLineCapStyle( it.value() );
1945  }
1946  else if ( it.key() == "stroke-dasharray" )
1947  {
1948  QVector<qreal> dashPattern = decodeSldRealVector( it.value() );
1949  if ( dashPattern.size() > 0 )
1950  {
1951  // convert the dasharray to one of the QT pen style,
1952  // if no match is found then set pen style to CustomDashLine
1953  bool dashPatternFound = false;
1954 
1955  if ( dashPattern.count() == 2 )
1956  {
1957  if ( dashPattern.at( 0 ) == 4.0 &&
1958  dashPattern.at( 1 ) == 2.0 )
1959  {
1960  penStyle = Qt::DashLine;
1961  dashPatternFound = true;
1962  }
1963  else if ( dashPattern.at( 0 ) == 1.0 &&
1964  dashPattern.at( 1 ) == 2.0 )
1965  {
1966  penStyle = Qt::DotLine;
1967  dashPatternFound = true;
1968  }
1969  }
1970  else if ( dashPattern.count() == 4 )
1971  {
1972  if ( dashPattern.at( 0 ) == 4.0 &&
1973  dashPattern.at( 1 ) == 2.0 &&
1974  dashPattern.at( 2 ) == 1.0 &&
1975  dashPattern.at( 3 ) == 2.0 )
1976  {
1977  penStyle = Qt::DashDotLine;
1978  dashPatternFound = true;
1979  }
1980  }
1981  else if ( dashPattern.count() == 6 )
1982  {
1983  if ( dashPattern.at( 0 ) == 4.0 &&
1984  dashPattern.at( 1 ) == 2.0 &&
1985  dashPattern.at( 2 ) == 1.0 &&
1986  dashPattern.at( 3 ) == 2.0 &&
1987  dashPattern.at( 4 ) == 1.0 &&
1988  dashPattern.at( 5 ) == 2.0 )
1989  {
1990  penStyle = Qt::DashDotDotLine;
1991  dashPatternFound = true;
1992  }
1993  }
1994 
1995  // default case: set pen style to CustomDashLine
1996  if ( !dashPatternFound )
1997  {
1998  if ( customDashPattern )
1999  {
2000  penStyle = Qt::CustomDashLine;
2001  *customDashPattern = dashPattern;
2002  }
2003  else
2004  {
2005  QgsDebugMsg( "custom dash pattern required but not provided. Using default dash pattern." );
2006  penStyle = Qt::DashLine;
2007  }
2008  }
2009  }
2010  }
2011  else if ( it.key() == "stroke-dashoffset" && dashOffset )
2012  {
2013  bool ok;
2014  double d = it.value().toDouble( &ok );
2015  if ( ok )
2016  *dashOffset = d;
2017  }
2018  }
2019 
2020  return true;
2021 }
2022 
2024  const QString& path, const QString& mime,
2025  const QColor& color, double size )
2026 {
2027  QDomElement externalGraphicElem = doc.createElement( "se:ExternalGraphic" );
2028  element.appendChild( externalGraphicElem );
2029 
2030  createOnlineResourceElement( doc, externalGraphicElem, path, mime );
2031 
2032  //TODO: missing a way to handle svg color. Should use <se:ColorReplacement>
2033  Q_UNUSED( color );
2034 
2035  if ( size >= 0 )
2036  {
2037  QDomElement sizeElem = doc.createElement( "se:Size" );
2038  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2039  element.appendChild( sizeElem );
2040  }
2041 }
2042 
2044  QString &path, QString &mime,
2045  QColor &color, double &size )
2046 {
2047  QgsDebugMsg( "Entered." );
2048  Q_UNUSED( color );
2049 
2050  QDomElement externalGraphicElem = element.firstChildElement( "ExternalGraphic" );
2051  if ( externalGraphicElem.isNull() )
2052  return false;
2053 
2054  onlineResourceFromSldElement( externalGraphicElem, path, mime );
2055 
2056  QDomElement sizeElem = element.firstChildElement( "Size" );
2057  if ( !sizeElem.isNull() )
2058  {
2059  bool ok;
2060  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2061  if ( ok )
2062  size = s;
2063  }
2064 
2065  return true;
2066 }
2067 
2069  const QString& path, const QString& format, int *markIndex,
2070  const QColor& color, double size )
2071 {
2072  QDomElement markElem = doc.createElement( "se:Mark" );
2073  element.appendChild( markElem );
2074 
2075  createOnlineResourceElement( doc, markElem, path, format );
2076 
2077  if ( markIndex )
2078  {
2079  QDomElement markIndexElem = doc.createElement( "se:MarkIndex" );
2080  markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) );
2081  markElem.appendChild( markIndexElem );
2082  }
2083 
2084  // <Fill>
2085  QDomElement fillElem = doc.createElement( "se:Fill" );
2086  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2087  markElem.appendChild( fillElem );
2088 
2089  // <Size>
2090  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
2091  {
2092  QDomElement sizeElem = doc.createElement( "se:Size" );
2093  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2094  element.appendChild( sizeElem );
2095  }
2096 }
2097 
2099  QString &path, QString &format, int &markIndex,
2100  QColor &color, double &size )
2101 {
2102  QgsDebugMsg( "Entered." );
2103 
2104  color = QColor();
2105  markIndex = -1;
2106  size = -1;
2107 
2108  QDomElement markElem = element.firstChildElement( "Mark" );
2109  if ( markElem.isNull() )
2110  return false;
2111 
2112  onlineResourceFromSldElement( markElem, path, format );
2113 
2114  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
2115  if ( !markIndexElem.isNull() )
2116  {
2117  bool ok;
2118  int i = markIndexElem.firstChild().nodeValue().toInt( &ok );
2119  if ( ok )
2120  markIndex = i;
2121  }
2122 
2123  // <Fill>
2124  QDomElement fillElem = markElem.firstChildElement( "Fill" );
2125  Qt::BrushStyle b = Qt::SolidPattern;
2126  fillFromSld( fillElem, b, color );
2127  // ignore brush style, solid expected
2128 
2129  // <Size>
2130  QDomElement sizeElem = element.firstChildElement( "Size" );
2131  if ( !sizeElem.isNull() )
2132  {
2133  bool ok;
2134  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2135  if ( ok )
2136  size = s;
2137  }
2138 
2139  return true;
2140 }
2141 
2143  const QString& name, const QColor& color, const QColor& borderColor,
2144  double borderWidth, double size )
2145 {
2146  wellKnownMarkerToSld( doc, element, name, color, borderColor, Qt::SolidLine, borderWidth, size );
2147 }
2148 
2150  const QString& name, const QColor& color, const QColor& borderColor, Qt::PenStyle borderStyle,
2151  double borderWidth, double size )
2152 {
2153  QDomElement markElem = doc.createElement( "se:Mark" );
2154  element.appendChild( markElem );
2155 
2156  QDomElement wellKnownNameElem = doc.createElement( "se:WellKnownName" );
2157  wellKnownNameElem.appendChild( doc.createTextNode( name ) );
2158  markElem.appendChild( wellKnownNameElem );
2159 
2160  // <Fill>
2161  if ( color.isValid() )
2162  {
2163  QDomElement fillElem = doc.createElement( "se:Fill" );
2164  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2165  markElem.appendChild( fillElem );
2166  }
2167 
2168  // <Stroke>
2169  if ( borderColor.isValid() )
2170  {
2171  QDomElement strokeElem = doc.createElement( "se:Stroke" );
2172  lineToSld( doc, strokeElem, borderStyle, borderColor, borderWidth );
2173  markElem.appendChild( strokeElem );
2174  }
2175 
2176  // <Size>
2177  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
2178  {
2179  QDomElement sizeElem = doc.createElement( "se:Size" );
2180  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2181  element.appendChild( sizeElem );
2182  }
2183 }
2184 
2186  QString &name, QColor &color, QColor &borderColor,
2187  double &borderWidth, double &size )
2188 {
2189  Qt::PenStyle borderStyle;
2190  return wellKnownMarkerFromSld( element, name, color, borderColor, borderStyle, borderWidth, size );
2191 }
2192 
2194  QString &name, QColor &color, QColor &borderColor, Qt::PenStyle &borderStyle,
2195  double &borderWidth, double &size )
2196 {
2197  QgsDebugMsg( "Entered." );
2198 
2199  name = "square";
2200  color = QColor();
2201  borderColor = QColor( "#000000" );
2202  borderWidth = 1;
2203  size = 6;
2204 
2205  QDomElement markElem = element.firstChildElement( "Mark" );
2206  if ( markElem.isNull() )
2207  return false;
2208 
2209  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
2210  if ( !wellKnownNameElem.isNull() )
2211  {
2212  name = wellKnownNameElem.firstChild().nodeValue();
2213  QgsDebugMsg( "found Mark with well known name: " + name );
2214  }
2215 
2216  // <Fill>
2217  QDomElement fillElem = markElem.firstChildElement( "Fill" );
2218  Qt::BrushStyle b = Qt::SolidPattern;
2219  fillFromSld( fillElem, b, color );
2220  // ignore brush style, solid expected
2221 
2222  // <Stroke>
2223  QDomElement strokeElem = markElem.firstChildElement( "Stroke" );
2224  lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
2225  // ignore border style, solid expected
2226 
2227  // <Size>
2228  QDomElement sizeElem = element.firstChildElement( "Size" );
2229  if ( !sizeElem.isNull() )
2230  {
2231  bool ok;
2232  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2233  if ( ok )
2234  size = s;
2235  }
2236 
2237  return true;
2238 }
2239 
2241 {
2242  if ( !rotationFunc.isEmpty() )
2243  {
2244  QDomElement rotationElem = doc.createElement( "se:Rotation" );
2245  createFunctionElement( doc, rotationElem, rotationFunc );
2246  element.appendChild( rotationElem );
2247  }
2248 }
2249 
2251 {
2252  QDomElement rotationElem = element.firstChildElement( "Rotation" );
2253  if ( !rotationElem.isNull() )
2254  {
2255  return functionFromSldElement( rotationElem, rotationFunc );
2256  }
2257  return true;
2258 }
2259 
2260 
2262 {
2263  if ( !alphaFunc.isEmpty() )
2264  {
2265  QDomElement opacityElem = doc.createElement( "se:Opacity" );
2266  createFunctionElement( doc, opacityElem, alphaFunc );
2267  element.appendChild( opacityElem );
2268  }
2269 }
2270 
2272 {
2273  QDomElement opacityElem = element.firstChildElement( "Opacity" );
2274  if ( !opacityElem.isNull() )
2275  {
2276  return functionFromSldElement( opacityElem, alphaFunc );
2277  }
2278  return true;
2279 }
2280 
2282 {
2283  if ( offset.isNull() )
2284  return;
2285 
2286  QDomElement displacementElem = doc.createElement( "se:Displacement" );
2287  element.appendChild( displacementElem );
2288 
2289  QDomElement dispXElem = doc.createElement( "se:DisplacementX" );
2290  dispXElem.appendChild( doc.createTextNode( QString::number( offset.x() ) ) );
2291 
2292  QDomElement dispYElem = doc.createElement( "se:DisplacementY" );
2293  dispYElem.appendChild( doc.createTextNode( QString::number( offset.y() ) ) );
2294 
2295  displacementElem.appendChild( dispXElem );
2296  displacementElem.appendChild( dispYElem );
2297 }
2298 
2300 {
2301  offset = QPointF( 0, 0 );
2302 
2303  QDomElement displacementElem = element.firstChildElement( "Displacement" );
2304  if ( displacementElem.isNull() )
2305  return true;
2306 
2307  QDomElement dispXElem = displacementElem.firstChildElement( "DisplacementX" );
2308  if ( !dispXElem.isNull() )
2309  {
2310  bool ok;
2311  double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok );
2312  if ( ok )
2313  offset.setX( offsetX );
2314  }
2315 
2316  QDomElement dispYElem = displacementElem.firstChildElement( "DisplacementY" );
2317  if ( !dispYElem.isNull() )
2318  {
2319  bool ok;
2320  double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok );
2321  if ( ok )
2322  offset.setY( offsetY );
2323  }
2324 
2325  return true;
2326 }
2327 
2329  const QString& label, const QFont& font,
2330  const QColor& color, double size )
2331 {
2332  QDomElement labelElem = doc.createElement( "se:Label" );
2333  labelElem.appendChild( doc.createTextNode( label ) );
2334  element.appendChild( labelElem );
2335 
2336  QDomElement fontElem = doc.createElement( "se:Font" );
2337  element.appendChild( fontElem );
2338 
2339  fontElem.appendChild( createSvgParameterElement( doc, "font-family", font.family() ) );
2340 #if 0
2341  fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) );
2342  fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) );
2343 #endif
2344  fontElem.appendChild( createSvgParameterElement( doc, "font-size", QString::number( size ) ) );
2345 
2346  // <Fill>
2347  if ( color.isValid() )
2348  {
2349  QDomElement fillElem = doc.createElement( "Fill" );
2350  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2351  element.appendChild( fillElem );
2352  }
2353 }
2354 
2355 QString QgsSymbolLayerV2Utils::ogrFeatureStylePen( double width, double mmScaleFactor, double mapUnitScaleFactor, const QColor& c,
2356  Qt::PenJoinStyle joinStyle,
2357  Qt::PenCapStyle capStyle,
2358  double offset,
2359  const QVector<qreal>* dashPattern )
2360 {
2361  QString penStyle;
2362  penStyle.append( "PEN(" );
2363  penStyle.append( "c:" );
2364  penStyle.append( c.name() );
2365  penStyle.append( ",w:" );
2366  //dxf driver writes ground units as mm? Should probably be changed in ogr
2367  penStyle.append( QString::number( width * mmScaleFactor ) );
2368  penStyle.append( "mm" );
2369 
2370  //dash dot vector
2371  if ( dashPattern && dashPattern->size() > 0 )
2372  {
2373  penStyle.append( ",p:\"" );
2374  QVector<qreal>::const_iterator pIt = dashPattern->constBegin();
2375  for ( ; pIt != dashPattern->constEnd(); ++pIt )
2376  {
2377  if ( pIt != dashPattern->constBegin() )
2378  {
2379  penStyle.append( " " );
2380  }
2381  penStyle.append( QString::number( *pIt * mapUnitScaleFactor ) );
2382  penStyle.append( "g" );
2383  }
2384  penStyle.append( "\"" );
2385  }
2386 
2387  //cap
2388  penStyle.append( ",cap:" );
2389  switch ( capStyle )
2390  {
2391  case Qt::SquareCap:
2392  penStyle.append( "p" );
2393  break;
2394  case Qt::RoundCap:
2395  penStyle.append( "r" );
2396  break;
2397  case Qt::FlatCap:
2398  default:
2399  penStyle.append( "b" );
2400  }
2401 
2402  //join
2403  penStyle.append( ",j:" );
2404  switch ( joinStyle )
2405  {
2406  case Qt::BevelJoin:
2407  penStyle.append( "b" );
2408  break;
2409  case Qt::RoundJoin:
2410  penStyle.append( "r" );
2411  break;
2412  case Qt::MiterJoin:
2413  default:
2414  penStyle.append( "m" );
2415  }
2416 
2417  //offset
2418  if ( !qgsDoubleNear( offset, 0.0 ) )
2419  {
2420  penStyle.append( ",dp:" );
2421  penStyle.append( QString::number( offset * mapUnitScaleFactor ) );
2422  penStyle.append( "g" );
2423  }
2424 
2425  penStyle.append( ")" );
2426  return penStyle;
2427 }
2428 
2430 {
2431  QString brushStyle;
2432  brushStyle.append( "BRUSH(" );
2433  brushStyle.append( "fc:" );
2434  brushStyle.append( fillColor.name() );
2435  brushStyle.append( ")" );
2436  return brushStyle;
2437 }
2438 
2440 {
2441  if ( geomFunc.isEmpty() )
2442  return;
2443 
2444  QDomElement geometryElem = doc.createElement( "Geometry" );
2445  element.appendChild( geometryElem );
2446 
2447  /* About using a function withing the Geometry tag.
2448  *
2449  * The SLD specification <= 1.1 is vague:
2450  * "In principle, a fixed geometry could be defined using GML or
2451  * operators could be defined for computing the geometry from
2452  * references or literals. However, using a feature property directly
2453  * is by far the most commonly useful method."
2454  *
2455  * Even if it seems that specs should take care all the possible cases,
2456  * looking at the XML schema fragment that encodes the Geometry element,
2457  * it has to be a PropertyName element:
2458  * <xsd:element name="Geometry">
2459  * <xsd:complexType>
2460  * <xsd:sequence>
2461  * <xsd:element ref="ogc:PropertyName"/>
2462  * </xsd:sequence>
2463  * </xsd:complexType>
2464  * </xsd:element>
2465  *
2466  * Anyway we will use a ogc:Function to handle geometry transformations
2467  * like offset, centroid, ...
2468  */
2469 
2470  createFunctionElement( doc, geometryElem, geomFunc );
2471 }
2472 
2474 {
2475  QDomElement geometryElem = element.firstChildElement( "Geometry" );
2476  if ( geometryElem.isNull() )
2477  return true;
2478 
2479  return functionFromSldElement( geometryElem, geomFunc );
2480 }
2481 
2483 {
2484  // let's use QgsExpression to generate the SLD for the function
2485  QgsExpression expr( function );
2486  if ( expr.hasParserError() )
2487  {
2488  element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
2489  return false;
2490  }
2491  QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
2492  if ( !filterElem.isNull() )
2493  element.appendChild( filterElem );
2494  return true;
2495 }
2496 
2498 {
2499  QDomElement elem = element;
2500  if ( element.tagName() != "Filter" )
2501  {
2502  QDomNodeList filterNodes = element.elementsByTagName( "Filter" );
2503  if ( filterNodes.size() > 0 )
2504  {
2505  elem = filterNodes.at( 0 ).toElement();
2506  }
2507  }
2508 
2509  if ( elem.isNull() )
2510  {
2511  return false;
2512  }
2513 
2514 
2516  if ( !expr )
2517  return false;
2518 
2519  bool valid = !expr->hasParserError();
2520  if ( !valid )
2521  {
2522  QgsDebugMsg( "parser error: " + expr->parserErrorString() );
2523  }
2524  else
2525  {
2526  function = expr->expression();
2527  }
2528 
2529  delete expr;
2530  return valid;
2531 }
2532 
2534  const QString& path, const QString& format )
2535 {
2536  // get resource url or relative path
2537  QString url = symbolPathToName( path );
2538  QDomElement onlineResourceElem = doc.createElement( "se:OnlineResource" );
2539  onlineResourceElem.setAttribute( "xlink:type", "simple" );
2540  onlineResourceElem.setAttribute( "xlink:href", url );
2541  element.appendChild( onlineResourceElem );
2542 
2543  QDomElement formatElem = doc.createElement( "se:Format" );
2544  formatElem.appendChild( doc.createTextNode( format ) );
2545  element.appendChild( formatElem );
2546 }
2547 
2549 {
2550  QgsDebugMsg( "Entered." );
2551 
2552  QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
2553  if ( onlineResourceElem.isNull() )
2554  return false;
2555 
2556  path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
2557 
2558  QDomElement formatElem = element.firstChildElement( "Format" );
2559  if ( formatElem.isNull() )
2560  return false; // OnlineResource requires a Format sibling element
2561 
2562  format = formatElem.firstChild().nodeValue();
2563  return true;
2564 }
2565 
2566 
2568 {
2569  QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
2570  nodeElem.setAttribute( "name", name );
2571  nodeElem.appendChild( doc.createTextNode( value ) );
2572  return nodeElem;
2573 }
2574 
2576 {
2577  QgsStringMap params;
2578 
2579  QDomElement paramElem = element.firstChildElement();
2580  while ( !paramElem.isNull() )
2581  {
2582  if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
2583  {
2584  QString name = paramElem.attribute( "name" );
2585  QString value = paramElem.firstChild().nodeValue();
2586 
2587  if ( !name.isEmpty() && !value.isEmpty() )
2588  params[ name ] = value;
2589  }
2590 
2591  paramElem = paramElem.nextSiblingElement();
2592  }
2593 
2594  return params;
2595 }
2596 
2598 {
2599  QDomElement nodeElem = doc.createElement( "VendorOption" );
2600  nodeElem.setAttribute( "name", name );
2601  nodeElem.appendChild( doc.createTextNode( value ) );
2602  return nodeElem;
2603 }
2604 
2606 {
2607  QgsStringMap params;
2608 
2609  QDomElement paramElem = element.firstChildElement( "VendorOption" );
2610  while ( !paramElem.isNull() )
2611  {
2612  QString name = paramElem.attribute( "name" );
2613  QString value = paramElem.firstChild().nodeValue();
2614 
2615  if ( !name.isEmpty() && !value.isEmpty() )
2616  params[ name ] = value;
2617 
2618  paramElem = paramElem.nextSiblingElement( "VendorOption" );
2619  }
2620 
2621  return params;
2622 }
2623 
2624 
2626 {
2627  QgsStringMap props;
2628  QDomElement e = element.firstChildElement();
2629  while ( !e.isNull() )
2630  {
2631  if ( e.tagName() != "prop" )
2632  {
2633  QgsDebugMsg( "unknown tag " + e.tagName() );
2634  }
2635  else
2636  {
2637  QString propKey = e.attribute( "k" );
2638  QString propValue = e.attribute( "v" );
2639  props[propKey] = propValue;
2640  }
2641  e = e.nextSiblingElement();
2642  }
2643  return props;
2644 }
2645 
2646 
2648 {
2649  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
2650  {
2651  QDomElement propEl = doc.createElement( "prop" );
2652  propEl.setAttribute( "k", it.key() );
2653  propEl.setAttribute( "v", it.value() );
2654  element.appendChild( propEl );
2655  }
2656 }
2657 
2659 {
2660  // go through symbols one-by-one and load them
2661 
2662  QgsSymbolV2Map symbols;
2663  QDomElement e = element.firstChildElement();
2664 
2665  while ( !e.isNull() )
2666  {
2667  if ( e.tagName() == "symbol" )
2668  {
2670  if ( symbol != NULL )
2671  symbols.insert( e.attribute( "name" ), symbol );
2672  }
2673  else
2674  {
2675  QgsDebugMsg( "unknown tag: " + e.tagName() );
2676  }
2677  e = e.nextSiblingElement();
2678  }
2679 
2680 
2681  // now walk through the list of symbols and find those prefixed with @
2682  // these symbols are sub-symbols of some other symbol layers
2683  // e.g. symbol named "@[email protected]" is sub-symbol of layer 1 in symbol "foo"
2684  QStringList subsymbols;
2685 
2686  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2687  {
2688  if ( it.key()[0] != '@' )
2689  continue;
2690 
2691  // add to array (for deletion)
2692  subsymbols.append( it.key() );
2693 
2694  QStringList parts = it.key().split( "@" );
2695  if ( parts.count() < 3 )
2696  {
2697  QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
2698  delete it.value(); // we must delete it
2699  continue; // some invalid syntax
2700  }
2701  QString symname = parts[1];
2702  int symlayer = parts[2].toInt();
2703 
2704  if ( !symbols.contains( symname ) )
2705  {
2706  QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
2707  delete it.value(); // we must delete it
2708  continue;
2709  }
2710 
2711  QgsSymbolV2* sym = symbols[symname];
2712  if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
2713  {
2714  QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
2715  delete it.value(); // we must delete it
2716  continue;
2717  }
2718 
2719  // set subsymbol takes ownership
2720  bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
2721  if ( !res )
2722  {
2723  QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
2724  }
2725 
2726 
2727  }
2728 
2729  // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
2730  for ( int i = 0; i < subsymbols.count(); i++ )
2731  symbols.take( subsymbols[i] );
2732 
2733  return symbols;
2734 }
2735 
2737 {
2738  QDomElement symbolsElem = doc.createElement( tagName );
2739 
2740  // save symbols
2741  for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its )
2742  {
2743  QDomElement symEl = saveSymbol( its.key(), its.value(), doc );
2744  symbolsElem.appendChild( symEl );
2745  }
2746 
2747  return symbolsElem;
2748 }
2749 
2751 {
2752  Q_FOREACH ( const QString& name, symbols.keys() )
2753  {
2754  delete symbols.value( name );
2755  }
2756  symbols.clear();
2757 }
2758 
2759 
2761 {
2762  QString rampType = element.attribute( "type" );
2763 
2764  // parse properties
2766 
2767  if ( rampType == "gradient" )
2768  return QgsVectorGradientColorRampV2::create( props );
2769  else if ( rampType == "random" )
2770  return QgsVectorRandomColorRampV2::create( props );
2771  else if ( rampType == "colorbrewer" )
2773  else if ( rampType == "cpt-city" )
2774  return QgsCptCityColorRampV2::create( props );
2775  else
2776  {
2777  QgsDebugMsg( "unknown colorramp type " + rampType );
2778  return NULL;
2779  }
2780 }
2781 
2782 
2784 {
2785  QDomElement rampEl = doc.createElement( "colorramp" );
2786  rampEl.setAttribute( "type", ramp->type() );
2787  rampEl.setAttribute( "name", name );
2788 
2789  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2790  return rampEl;
2791 }
2792 
2794 {
2795  if ( !color.isValid() )
2796  {
2797  return QString();
2798  }
2799 
2800  //TODO - utilise a color names database (such as X11) to return nicer names
2801  //for now, just return hex codes
2802  return color.name();
2803 }
2804 
2806 {
2807  QList<QColor> colors;
2808 
2809  //try splitting string at commas, spaces or newlines
2810  QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
2811  QStringList::iterator it = components.begin();
2812  for ( ; it != components.end(); ++it )
2813  {
2814  QColor result = parseColor( *it, true );
2815  if ( result.isValid() )
2816  {
2817  colors << result;
2818  }
2819  }
2820  if ( colors.length() > 0 )
2821  {
2822  return colors;
2823  }
2824 
2825  //try splitting string at commas or newlines
2826  components = colorStr.split( QRegExp( "(,|\n)" ) );
2827  it = components.begin();
2828  for ( ; it != components.end(); ++it )
2829  {
2830  QColor result = parseColor( *it, true );
2831  if ( result.isValid() )
2832  {
2833  colors << result;
2834  }
2835  }
2836  if ( colors.length() > 0 )
2837  {
2838  return colors;
2839  }
2840 
2841  //try splitting string at whitespace or newlines
2842  components = colorStr.simplified().split( QString( " " ) );
2843  it = components.begin();
2844  for ( ; it != components.end(); ++it )
2845  {
2846  QColor result = parseColor( *it, true );
2847  if ( result.isValid() )
2848  {
2849  colors << result;
2850  }
2851  }
2852  if ( colors.length() > 0 )
2853  {
2854  return colors;
2855  }
2856 
2857  //try splitting string just at newlines
2858  components = colorStr.split( QString( "\n" ) );
2859  it = components.begin();
2860  for ( ; it != components.end(); ++it )
2861  {
2862  QColor result = parseColor( *it, true );
2863  if ( result.isValid() )
2864  {
2865  colors << result;
2866  }
2867  }
2868 
2869  return colors;
2870 }
2871 
2873 {
2874  //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
2875  //value, and can be used when pasting colors outside of QGIS).
2876  QMimeData *mimeData = new QMimeData;
2877  mimeData->setColorData( QVariant( color ) );
2878  mimeData->setText( color.name() );
2879  return mimeData;
2880 }
2881 
2882 QColor QgsSymbolLayerV2Utils::colorFromMimeData( const QMimeData * mimeData, bool& hasAlpha )
2883 {
2884  //attempt to read color data directly from mime
2885  QColor mimeColor = mimeData->colorData().value<QColor>();
2886  if ( mimeColor.isValid() )
2887  {
2888  hasAlpha = true;
2889  return mimeColor;
2890  }
2891 
2892  //attempt to intrepret a color from mime text data
2893  hasAlpha = false;
2894  QColor textColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( mimeData->text(), hasAlpha );
2895  if ( textColor.isValid() )
2896  {
2897  return textColor;
2898  }
2899 
2900  //could not get color from mime data
2901  return QColor();
2902 }
2903 
2905 {
2906  QgsNamedColorList mimeColors;
2907 
2908  //prefer xml format
2909  if ( data->hasFormat( "text/xml" ) )
2910  {
2911  //get XML doc
2912  QByteArray encodedData = data->data( "text/xml" );
2913  QDomDocument xmlDoc;
2914  xmlDoc.setContent( encodedData );
2915 
2916  QDomElement dragDataElem = xmlDoc.documentElement();
2917  if ( dragDataElem.tagName() == "ColorSchemeModelDragData" )
2918  {
2919  QDomNodeList nodeList = dragDataElem.childNodes();
2920  int nChildNodes = nodeList.size();
2921  QDomElement currentElem;
2922 
2923  for ( int i = 0; i < nChildNodes; ++i )
2924  {
2925  currentElem = nodeList.at( i ).toElement();
2926  if ( currentElem.isNull() )
2927  {
2928  continue;
2929  }
2930 
2931  QPair< QColor, QString> namedColor;
2932  namedColor.first = QgsSymbolLayerV2Utils::decodeColor( currentElem.attribute( "color", "255,255,255,255" ) );
2933  namedColor.second = currentElem.attribute( "label", "" );
2934 
2935  mimeColors << namedColor;
2936  }
2937  }
2938  }
2939 
2940  if ( mimeColors.length() == 0 && data->hasFormat( "application/x-colorobject-list" ) )
2941  {
2942  //get XML doc
2943  QByteArray encodedData = data->data( "application/x-colorobject-list" );
2944  QDomDocument xmlDoc;
2945  xmlDoc.setContent( encodedData );
2946 
2947  QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QString( "colors" ) );
2948  if ( colorsNodes.length() > 0 )
2949  {
2950  QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
2951  QDomNodeList colorNodeList = colorsElem.childNodes();
2952  int nChildNodes = colorNodeList.size();
2953  QDomElement currentElem;
2954 
2955  for ( int i = 0; i < nChildNodes; ++i )
2956  {
2957  //li element
2958  currentElem = colorNodeList.at( i ).toElement();
2959  if ( currentElem.isNull() )
2960  {
2961  continue;
2962  }
2963 
2964  QDomNodeList colorNodes = currentElem.elementsByTagName( QString( "color" ) );
2965  QDomNodeList nameNodes = currentElem.elementsByTagName( QString( "name" ) );
2966 
2967  if ( colorNodes.length() > 0 )
2968  {
2969  QDomElement colorElem = colorNodes.at( 0 ).toElement();
2970 
2971  QStringList colorParts = colorElem.text().simplified().split( " " );
2972  if ( colorParts.length() < 3 )
2973  {
2974  continue;
2975  }
2976 
2977  int red = colorParts.at( 0 ).toDouble() * 255;
2978  int green = colorParts.at( 1 ).toDouble() * 255;
2979  int blue = colorParts.at( 2 ).toDouble() * 255;
2980  QPair< QColor, QString> namedColor;
2981  namedColor.first = QColor( red, green, blue );
2982  if ( nameNodes.length() > 0 )
2983  {
2984  QDomElement nameElem = nameNodes.at( 0 ).toElement();
2985  namedColor.second = nameElem.text();
2986  }
2987  mimeColors << namedColor;
2988  }
2989  }
2990  }
2991  }
2992 
2993  if ( mimeColors.length() == 0 && data->hasText() )
2994  {
2995  //attempt to read color data from mime text
2997  QList< QColor >::iterator it = parsedColors.begin();
2998  for ( ; it != parsedColors.end(); ++it )
2999  {
3000  mimeColors << qMakePair( *it, QString() );
3001  }
3002  }
3003 
3004  if ( mimeColors.length() == 0 && data->hasColor() )
3005  {
3006  //attempt to read color data directly from mime
3007  QColor mimeColor = data->colorData().value<QColor>();
3008  if ( mimeColor.isValid() )
3009  {
3010  mimeColors << qMakePair( mimeColor, QString() );
3011  }
3012  }
3013 
3014  return mimeColors;
3015 }
3016 
3018 {
3019  //native format
3020  QMimeData* mimeData = new QMimeData();
3021  QDomDocument xmlDoc;
3022  QDomElement xmlRootElement = xmlDoc.createElement( "ColorSchemeModelDragData" );
3023  xmlDoc.appendChild( xmlRootElement );
3024 
3025  QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
3026  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3027  {
3028  QDomElement namedColor = xmlDoc.createElement( "NamedColor" );
3029  namedColor.setAttribute( "color", QgsSymbolLayerV2Utils::encodeColor(( *colorIt ).first ) );
3030  namedColor.setAttribute( "label", ( *colorIt ).second );
3031  xmlRootElement.appendChild( namedColor );
3032  }
3033  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
3034 
3035  if ( !allFormats )
3036  {
3037  return mimeData;
3038  }
3039 
3040  //set mime text to list of hex values
3041  colorIt = colorList.constBegin();
3042  QStringList colorListString;
3043  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3044  {
3045  colorListString << ( *colorIt ).first.name();
3046  }
3047  mimeData->setText( colorListString.join( "\n" ) );
3048 
3049  //set mime color data to first color
3050  if ( colorList.length() > 0 )
3051  {
3052  mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
3053  }
3054 
3055  return mimeData;
3056 }
3057 
3058 bool QgsSymbolLayerV2Utils::saveColorsToGpl( QFile &file, const QString& paletteName, const QgsNamedColorList& colors )
3059 {
3060  if ( !file.open( QIODevice::ReadWrite ) )
3061  {
3062  return false;
3063  }
3064 
3065  QTextStream stream( &file );
3066  stream << "GIMP Palette" << endl;
3067  if ( paletteName.isEmpty() )
3068  {
3069  stream << "Name: QGIS Palette" << endl;
3070  }
3071  else
3072  {
3073  stream << "Name: " << paletteName << endl;
3074  }
3075  stream << "Columns: 4" << endl;
3076  stream << "#" << endl;
3077 
3078  for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
3079  {
3080  QColor color = ( *colorIt ).first;
3081  if ( !color.isValid() )
3082  {
3083  continue;
3084  }
3085  stream << QString( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
3086  stream << "\t" << (( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
3087  }
3088  file.close();
3089 
3090  return true;
3091 }
3092 
3094 {
3095  QgsNamedColorList importedColors;
3096 
3097  if ( !file.open( QIODevice::ReadOnly ) )
3098  {
3099  ok = false;
3100  return importedColors;
3101  }
3102 
3103  QTextStream in( &file );
3104 
3105  QString line = in.readLine();
3106  if ( !line.startsWith( "GIMP Palette" ) )
3107  {
3108  ok = false;
3109  return importedColors;
3110  }
3111 
3112  //find name line
3113  while ( !in.atEnd() && !line.startsWith( "Name:" ) && !line.startsWith( "#" ) )
3114  {
3115  line = in.readLine();
3116  }
3117  if ( line.startsWith( "Name:" ) )
3118  {
3119  QRegExp nameRx( "Name:\\s*(\\S.*)$" );
3120  if ( nameRx.indexIn( line ) != -1 )
3121  {
3122  name = nameRx.cap( 1 );
3123  }
3124  }
3125 
3126  //ignore lines until after "#"
3127  while ( !in.atEnd() && !line.startsWith( "#" ) )
3128  {
3129  line = in.readLine();
3130  }
3131  if ( in.atEnd() )
3132  {
3133  ok = false;
3134  return importedColors;
3135  }
3136 
3137  //ready to start reading colors
3138  QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
3139  while ( !in.atEnd() )
3140  {
3141  line = in.readLine();
3142  if ( rx.indexIn( line ) == -1 )
3143  {
3144  continue;
3145  }
3146  int red = rx.cap( 1 ).toInt();
3147  int green = rx.cap( 2 ).toInt();
3148  int blue = rx.cap( 3 ).toInt();
3149  QColor color = QColor( red, green, blue );
3150  if ( !color.isValid() )
3151  {
3152  continue;
3153  }
3154 
3155  //try to read color name
3156  QString label;
3157  if ( rx.captureCount() > 3 )
3158  {
3159  label = rx.cap( 4 ).simplified();
3160  }
3161  else
3162  {
3163  label = colorToName( color );
3164  }
3165 
3166  importedColors << qMakePair( color, label );
3167  }
3168 
3169  file.close();
3170  ok = true;
3171  return importedColors;
3172 }
3173 
3174 QColor QgsSymbolLayerV2Utils::parseColor( const QString& colorStr, bool strictEval )
3175 {
3176  bool hasAlpha;
3177  return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
3178 }
3179 
3180 QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString& colorStr, bool &containsAlpha, bool strictEval )
3181 {
3182  QColor parsedColor;
3183 
3184  //color in hex format "#aabbcc"
3185  if ( QColor::isValidColor( colorStr ) )
3186  {
3187  //string is a valid hex color string
3188  parsedColor.setNamedColor( colorStr );
3189  if ( parsedColor.isValid() )
3190  {
3191  containsAlpha = false;
3192  return parsedColor;
3193  }
3194  }
3195 
3196  //color in hex format, with alpha
3197  QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
3198  if ( hexColorAlphaRx.indexIn( colorStr ) != -1 )
3199  {
3200  QString hexColor = hexColorAlphaRx.cap( 1 );
3201  parsedColor.setNamedColor( QString( "#" ) + hexColor );
3202  bool alphaOk;
3203  int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
3204 
3205  if ( parsedColor.isValid() && alphaOk )
3206  {
3207  parsedColor.setAlpha( alphaHex );
3208  containsAlpha = true;
3209  return parsedColor;
3210  }
3211  }
3212 
3213  if ( !strictEval )
3214  {
3215  //color in hex format, without #
3216  QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
3217  if ( hexColorRx2.indexIn( colorStr ) != -1 )
3218  {
3219  //add "#" and parse
3220  parsedColor.setNamedColor( QString( "#" ) + colorStr );
3221  if ( parsedColor.isValid() )
3222  {
3223  containsAlpha = false;
3224  return parsedColor;
3225  }
3226  }
3227  }
3228 
3229  //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
3230  QRegExp rgbFormatRx( "^\\s*(?:rgb)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*\\)?\\s*;?\\s*$" );
3231  if ( rgbFormatRx.indexIn( colorStr ) != -1 )
3232  {
3233  int r = rgbFormatRx.cap( 1 ).toInt();
3234  int g = rgbFormatRx.cap( 2 ).toInt();
3235  int b = rgbFormatRx.cap( 3 ).toInt();
3236  parsedColor.setRgb( r, g, b );
3237  if ( parsedColor.isValid() )
3238  {
3239  containsAlpha = false;
3240  return parsedColor;
3241  }
3242  }
3243 
3244  //color in (r%,g%,b%) format, brackets and rgb prefix optional
3245  QRegExp rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" );
3246  if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
3247  {
3248  int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3249  int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3250  int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3251  parsedColor.setRgb( r, g, b );
3252  if ( parsedColor.isValid() )
3253  {
3254  containsAlpha = false;
3255  return parsedColor;
3256  }
3257  }
3258 
3259  //color in (r,g,b,a) format, brackets and rgba prefix optional
3260  QRegExp rgbaFormatRx( "^\\s*(?:rgba)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
3261  if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
3262  {
3263  int r = rgbaFormatRx.cap( 1 ).toInt();
3264  int g = rgbaFormatRx.cap( 2 ).toInt();
3265  int b = rgbaFormatRx.cap( 3 ).toInt();
3266  int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
3267  parsedColor.setRgb( r, g, b, a );
3268  if ( parsedColor.isValid() )
3269  {
3270  containsAlpha = true;
3271  return parsedColor;
3272  }
3273  }
3274 
3275  //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
3276  QRegExp rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
3277  if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
3278  {
3279  int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3280  int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3281  int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3282  int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
3283  parsedColor.setRgb( r, g, b, a );
3284  if ( parsedColor.isValid() )
3285  {
3286  containsAlpha = true;
3287  return parsedColor;
3288  }
3289  }
3290 
3291  //couldn't parse string as color
3292  return QColor();
3293 }
3294 
3296 {
3297  switch ( u )
3298  {
3299  case QgsSymbolV2::MM:
3300  return c.scaleFactor();
3301  case QgsSymbolV2::MapUnit:
3302  {
3303  double mup = scale.computeMapUnitsPerPixel( c );
3304  if ( mup > 0 )
3305  {
3306  return 1.0 / mup;
3307  }
3308  else
3309  {
3310  return 1.0;
3311  }
3312  }
3313  case QgsSymbolV2::Pixel:
3314  return 1.0 / c.rasterScaleFactor();
3315  case QgsSymbolV2::Mixed:
3316  //no sensible value
3317  return 1.0;
3318  }
3319  return 1.0;
3320 }
3321 
3323 {
3324  double conversionFactor = lineWidthScaleFactor( c, unit, scale );
3325  double convertedSize = size * conversionFactor;
3326 
3327  if ( unit == QgsSymbolV2::MapUnit )
3328  {
3329  //check max/min size
3330  if ( scale.minSizeMMEnabled )
3331  convertedSize = qMax( convertedSize, scale.minSizeMM * c.scaleFactor() );
3332  if ( scale.maxSizeMMEnabled )
3333  convertedSize = qMin( convertedSize, scale.maxSizeMM * c.scaleFactor() );
3334  }
3335 
3336  return convertedSize;
3337 }
3338 
3340 {
3341  switch ( u )
3342  {
3343  case QgsSymbolV2::MM:
3344  return ( c.scaleFactor() * c.rasterScaleFactor() );
3345  case QgsSymbolV2::MapUnit:
3346  {
3347  double mup = scale.computeMapUnitsPerPixel( c );
3348  if ( mup > 0 )
3349  {
3350  return c.rasterScaleFactor() / mup;
3351  }
3352  else
3353  {
3354  return 1.0;
3355  }
3356  }
3357  case QgsSymbolV2::Pixel:
3358  return 1.0;
3359  case QgsSymbolV2::Mixed:
3360  //no sensible value
3361  return 1.0;
3362  }
3363  return 1.0;
3364 }
3365 
3367 {
3368  switch ( u )
3369  {
3370  case QgsSymbolV2::MM:
3371  return scale.computeMapUnitsPerPixel( c ) * c.scaleFactor() * c.rasterScaleFactor();
3372  case QgsSymbolV2::MapUnit:
3373  {
3374  return 1.0;
3375  }
3376  case QgsSymbolV2::Pixel:
3377  return scale.computeMapUnitsPerPixel( c );
3378  case QgsSymbolV2::Mixed:
3379  //no sensible value
3380  return 1.0;
3381  }
3382  return 1.0;
3383 }
3384 
3386 {
3387  QgsRenderContext context;
3388  context.setPainter( p );
3389  context.setRasterScaleFactor( 1.0 );
3390  if ( p && p->device() )
3391  {
3392  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
3393  }
3394  else
3395  {
3396  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
3397  }
3398  return context;
3399 }
3400 
3402 {
3403  if ( !image )
3404  {
3405  return;
3406  }
3407 
3408  QRgb myRgb;
3409  QImage::Format format = image->format();
3410  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
3411  {
3412  QgsDebugMsg( "no alpha channel." );
3413  return;
3414  }
3415 
3416  //change the alpha component of every pixel
3417  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
3418  {
3419  QRgb* scanLine = ( QRgb* )image->scanLine( heightIndex );
3420  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
3421  {
3422  myRgb = scanLine[widthIndex];
3423  if ( format == QImage::Format_ARGB32_Premultiplied )
3424  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3425  else
3426  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3427  }
3428  }
3429 }
3430 
3431 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, const QRect& rect, int radius, bool alphaOnly )
3432 {
3433  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
3434  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
3435  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
3436 
3437  if ( image.format() != QImage::Format_ARGB32_Premultiplied
3438  && image.format() != QImage::Format_RGB32 )
3439  {
3440  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
3441  }
3442 
3443  int r1 = rect.top();
3444  int r2 = rect.bottom();
3445  int c1 = rect.left();
3446  int c2 = rect.right();
3447 
3448  int bpl = image.bytesPerLine();
3449  int rgba[4];
3450  unsigned char* p;
3451 
3452  int i1 = 0;
3453  int i2 = 3;
3454 
3455  if ( alphaOnly ) // this seems to only work right for a black color
3456  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
3457 
3458  for ( int col = c1; col <= c2; col++ )
3459  {
3460  p = image.scanLine( r1 ) + col * 4;
3461  for ( int i = i1; i <= i2; i++ )
3462  rgba[i] = p[i] << 4;
3463 
3464  p += bpl;
3465  for ( int j = r1; j < r2; j++, p += bpl )
3466  for ( int i = i1; i <= i2; i++ )
3467  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3468  }
3469 
3470  for ( int row = r1; row <= r2; row++ )
3471  {
3472  p = image.scanLine( row ) + c1 * 4;
3473  for ( int i = i1; i <= i2; i++ )
3474  rgba[i] = p[i] << 4;
3475 
3476  p += 4;
3477  for ( int j = c1; j < c2; j++, p += 4 )
3478  for ( int i = i1; i <= i2; i++ )
3479  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3480  }
3481 
3482  for ( int col = c1; col <= c2; col++ )
3483  {
3484  p = image.scanLine( r2 ) + col * 4;
3485  for ( int i = i1; i <= i2; i++ )
3486  rgba[i] = p[i] << 4;
3487 
3488  p -= bpl;
3489  for ( int j = r1; j < r2; j++, p -= bpl )
3490  for ( int i = i1; i <= i2; i++ )
3491  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3492  }
3493 
3494  for ( int row = r1; row <= r2; row++ )
3495  {
3496  p = image.scanLine( row ) + c2 * 4;
3497  for ( int i = i1; i <= i2; i++ )
3498  rgba[i] = p[i] << 4;
3499 
3500  p -= 4;
3501  for ( int j = c1; j < c2; j++, p -= 4 )
3502  for ( int i = i1; i <= i2; i++ )
3503  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3504  }
3505 }
3506 
3508 {
3509  if ( alpha != 255 && alpha > 0 )
3510  {
3511  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3512  // where color values have to be premultiplied by alpha
3513  double alphaFactor = alpha / 255.;
3514  int r = 0, g = 0, b = 0;
3515  rgb.getRgb( &r, &g, &b );
3516 
3517  r *= alphaFactor;
3518  g *= alphaFactor;
3519  b *= alphaFactor;
3520  rgb.setRgb( r, g, b, alpha );
3521  }
3522  else if ( alpha == 0 )
3523  {
3524  rgb.setRgb( 0, 0, 0, 0 );
3525  }
3526 }
3527 
3528 #if 0
3529 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
3530 {
3531  switch ( lhs.type() )
3532  {
3533  case QVariant::Int:
3534  return lhs.toInt() < rhs.toInt();
3535  case QVariant::UInt:
3536  return lhs.toUInt() < rhs.toUInt();
3537  case QVariant::LongLong:
3538  return lhs.toLongLong() < rhs.toLongLong();
3539  case QVariant::ULongLong:
3540  return lhs.toULongLong() < rhs.toULongLong();
3541  case QVariant::Double:
3542  return lhs.toDouble() < rhs.toDouble();
3543  case QVariant::Char:
3544  return lhs.toChar() < rhs.toChar();
3545  case QVariant::Date:
3546  return lhs.toDate() < rhs.toDate();
3547  case QVariant::Time:
3548  return lhs.toTime() < rhs.toTime();
3549  case QVariant::DateTime:
3550  return lhs.toDateTime() < rhs.toDateTime();
3551  default:
3552  return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
3553  }
3554 }
3555 
3556 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
3557 {
3558  return ! _QVariantLessThan( lhs, rhs );
3559 }
3560 #endif
3561 
3563 {
3564  if ( order == Qt::AscendingOrder )
3565  {
3566  //qSort( list.begin(), list.end(), _QVariantLessThan );
3567  qSort( list.begin(), list.end(), qgsVariantLessThan );
3568  }
3569  else // Qt::DescendingOrder
3570  {
3571  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
3572  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
3573  }
3574 }
3575 
3576 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance )
3577 {
3578  double dx = directionPoint.x() - startPoint.x();
3579  double dy = directionPoint.y() - startPoint.y();
3580  double length = sqrt( dx * dx + dy * dy );
3581  double scaleFactor = distance / length;
3582  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
3583 }
3584 
3585 
3587 {
3588  // copied from QgsMarkerCatalogue - TODO: unify
3589  QStringList list;
3591 
3592  for ( int i = 0; i < svgPaths.size(); i++ )
3593  {
3594  QDir dir( svgPaths[i] );
3595  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3596  {
3597  svgPaths.insert( i + 1, dir.path() + "/" + item );
3598  }
3599 
3600  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3601  {
3602  // TODO test if it is correct SVG
3603  list.append( dir.path() + "/" + item );
3604  }
3605  }
3606  return list;
3607 }
3608 
3609 // Stripped down version of listSvgFiles() for specified directory
3611 {
3612  // TODO anything that applies for the listSvgFiles() applies this also
3613 
3614  QStringList list;
3615  QStringList svgPaths;
3616  svgPaths.append( directory );
3617 
3618  for ( int i = 0; i < svgPaths.size(); i++ )
3619  {
3620  QDir dir( svgPaths[i] );
3621  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3622  {
3623  svgPaths.insert( i + 1, dir.path() + "/" + item );
3624  }
3625 
3626  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3627  {
3628  list.append( dir.path() + "/" + item );
3629  }
3630  }
3631  return list;
3632 
3633 }
3634 
3636 {
3637  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
3638 
3639  // we might have a full path...
3640  if ( QFile( name ).exists() )
3641  return QFileInfo( name ).canonicalFilePath();
3642 
3643  // or it might be an url...
3644  if ( name.contains( "://" ) )
3645  {
3646  QUrl url( name );
3647  if ( url.isValid() && !url.scheme().isEmpty() )
3648  {
3649  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
3650  {
3651  // it's a url to a local file
3652  name = url.toLocalFile();
3653  if ( QFile( name ).exists() )
3654  {
3655  return QFileInfo( name ).canonicalFilePath();
3656  }
3657  }
3658  else
3659  {
3660  // it's a url pointing to a online resource
3661  return name;
3662  }
3663  }
3664  }
3665 
3666  // SVG symbol not found - probably a relative path was used
3667 
3669  for ( int i = 0; i < svgPaths.size(); i++ )
3670  {
3671  QString svgPath = svgPaths[i];
3672  if ( svgPath.endsWith( QChar( '/' ) ) )
3673  {
3674  svgPath.chop( 1 );
3675  }
3676 
3677  QgsDebugMsg( "SvgPath: " + svgPath );
3678  // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
3679  //QFileInfo myInfo( name );
3680  //QString myFileName = myInfo.fileName(); // foo.svg
3681  //QString myLowestDir = myInfo.dir().dirName();
3682  //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : "/" + myLowestDir ) + "/" + myFileName;
3683  QString myLocalPath = svgPath + QDir::separator() + name;
3684 
3685  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
3686  if ( QFile( myLocalPath ).exists() )
3687  {
3688  QgsDebugMsg( "Svg found in alternative path" );
3689  return QFileInfo( myLocalPath ).canonicalFilePath();
3690  }
3691  }
3692 
3693  QFileInfo pfi( QgsProject::instance()->fileName() );
3694  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
3695  if ( pfi.exists() && QFile( alternatePath ).exists() )
3696  {
3697  QgsDebugMsg( "Svg found in alternative path" );
3698  return QFileInfo( alternatePath ).canonicalFilePath();
3699  }
3700  else
3701  {
3702  QgsDebugMsg( "Svg not found in project path" );
3703  }
3704  //couldnt find the file, no happy ending :-(
3705  QgsDebugMsg( "Computed alternate path but no svg there either" );
3706 
3707  return QString();
3708 }
3709 
3711 {
3712  // copied from QgsSymbol::writeXML
3713 
3714  QFileInfo fi( path );
3715  if ( !fi.exists() )
3716  return path;
3717 
3718  path = fi.canonicalFilePath();
3719 
3721 
3722  bool isInSvgPathes = false;
3723  for ( int i = 0; i < svgPaths.size(); i++ )
3724  {
3725  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
3726 
3727  if ( !dir.isEmpty() && path.startsWith( dir ) )
3728  {
3729  path = path.mid( dir.size() + 1 );
3730  isInSvgPathes = true;
3731  break;
3732  }
3733  }
3734 
3735  if ( isInSvgPathes )
3736  return path;
3737 
3738  return QgsProject::instance()->writePath( path );
3739 }
3740 
3742 {
3743  //Calculate the centroid of points
3744  double cx = 0, cy = 0;
3745  double area, sum = 0;
3746  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
3747  {
3748  const QPointF& p1 = points[i];
3749  const QPointF& p2 = points[j];
3750  area = p1.x() * p2.y() - p1.y() * p2.x();
3751  sum += area;
3752  cx += ( p1.x() + p2.x() ) * area;
3753  cy += ( p1.y() + p2.y() ) * area;
3754  }
3755  sum *= 3.0;
3756  if ( sum == 0 )
3757  {
3758  // the linear ring is invalid - let's fall back to a solution that will still
3759  // allow us render at least something (instead of just returning point nan,nan)
3760  if ( points.count() >= 2 )
3761  return QPointF(( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
3762  else if ( points.count() == 1 )
3763  return points[0];
3764  else
3765  return QPointF(); // hopefully we shouldn't ever get here
3766  }
3767  cx /= sum;
3768  cy /= sum;
3769 
3770  return QPointF( cx, cy );
3771 }
3772 
3774 {
3775  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3776 
3777  // check if centroid inside in polygon
3778  if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3779  {
3780  unsigned int i, pointCount = points.count();
3781 
3782  QgsPolyline polyline( pointCount );
3783  for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3784 
3785  QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3786  if ( geom )
3787  {
3788  QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3789 
3790  if ( pointOnSurfaceGeom )
3791  {
3792  QgsPoint point = pointOnSurfaceGeom->asPoint();
3793  delete pointOnSurfaceGeom;
3794  delete geom;
3795 
3796  return QPointF( point.x(), point.y() );
3797  }
3798  delete geom;
3799  }
3800  }
3801  return centroid;
3802 }
3803 
3804 bool QgsSymbolLayerV2Utils::pointInPolygon( const QPolygonF &points, const QPointF &point )
3805 {
3806  bool inside = false;
3807 
3808  double x = point.x();
3809  double y = point.y();
3810 
3811  for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3812  {
3813  const QPointF& p1 = points[i];
3814  const QPointF& p2 = points[j];
3815 
3816  if ( p1.x() == x && p1.y() == y )
3817  return true;
3818 
3819  if (( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3820  {
3821  if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3822  inside = !inside;
3823  }
3824 
3825  j = i;
3826  }
3827  return inside;
3828 }
3829 
3831 {
3832  if ( fieldOrExpression.isEmpty() )
3833  return 0;
3834 
3835  QgsExpression* expr = new QgsExpression( fieldOrExpression );
3836  if ( !expr->hasParserError() )
3837  return expr;
3838 
3839  // now try with quoted field name
3840  delete expr;
3841  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
3842  Q_ASSERT( !expr2->hasParserError() );
3843  return expr2;
3844 }
3845 
3847 {
3848  const QgsExpression::Node* n = expression->rootNode();
3849 
3850  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
3851  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
3852 
3853  return expression->expression();
3854 }
3855 
3856 QList<double> QgsSymbolLayerV2Utils::prettyBreaks( double minimum, double maximum, int classes )
3857 {
3858  // C++ implementation of R's pretty algorithm
3859  // Based on code for determining optimal tick placement for statistical graphics
3860  // from the R statistical programming language.
3861  // Code ported from R implementation from 'labeling' R package
3862  //
3863  // Computes a sequence of about 'classes' equally spaced round values
3864  // which cover the range of values from 'minimum' to 'maximum'.
3865  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
3866 
3867  QList<double> breaks;
3868  if ( classes < 1 )
3869  {
3870  breaks.append( maximum );
3871  return breaks;
3872  }
3873 
3874  int minimumCount = ( int ) classes / 3;
3875  double shrink = 0.75;
3876  double highBias = 1.5;
3877  double adjustBias = 0.5 + 1.5 * highBias;
3878  int divisions = classes;
3879  double h = highBias;
3880  double cell;
3881  int U;
3882  bool small = false;
3883  double dx = maximum - minimum;
3884 
3885  if ( dx == 0 && maximum == 0 )
3886  {
3887  cell = 1.0;
3888  small = true;
3889  U = 1;
3890  }
3891  else
3892  {
3893  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
3894  if ( adjustBias >= 1.5 * h + 0.5 )
3895  {
3896  U = 1 + ( 1.0 / ( 1 + h ) );
3897  }
3898  else
3899  {
3900  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
3901  }
3902  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
3903  }
3904 
3905  if ( small )
3906  {
3907  if ( cell > 10 )
3908  {
3909  cell = 9 + cell / 10;
3910  cell = cell * shrink;
3911  }
3912  if ( minimumCount > 1 )
3913  {
3914  cell = cell / minimumCount;
3915  }
3916  }
3917  else
3918  {
3919  cell = dx;
3920  if ( divisions > 1 )
3921  {
3922  cell = cell / divisions;
3923  }
3924  }
3925  if ( cell < 20 * 1e-07 )
3926  {
3927  cell = 20 * 1e-07;
3928  }
3929 
3930  double base = pow( 10.0, floor( log10( cell ) ) );
3931  double unit = base;
3932  if (( 2 * base ) - cell < h *( cell - unit ) )
3933  {
3934  unit = 2.0 * base;
3935  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
3936  {
3937  unit = 5.0 * base;
3938  if (( 10.0 * base ) - cell < h *( cell - unit ) )
3939  {
3940  unit = 10.0 * base;
3941  }
3942  }
3943  }
3944  // Maybe used to correct for the epsilon here??
3945  int start = floor( minimum / unit + 1e-07 );
3946  int end = ceil( maximum / unit - 1e-07 );
3947 
3948  // Extend the range out beyond the data. Does this ever happen??
3949  while ( start * unit > minimum + ( 1e-07 * unit ) )
3950  {
3951  start = start - 1;
3952  }
3953  while ( end * unit < maximum - ( 1e-07 * unit ) )
3954  {
3955  end = end + 1;
3956  }
3957  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
3958 
3959  // If we don't have quite enough labels, extend the range out
3960  // to make more (these labels are beyond the data :( )
3961  int k = floor( 0.5 + end - start );
3962  if ( k < minimumCount )
3963  {
3964  k = minimumCount - k;
3965  if ( start >= 0 )
3966  {
3967  end = end + k / 2;
3968  start = start - k / 2 + k % 2;
3969  }
3970  else
3971  {
3972  start = start - k / 2;
3973  end = end + k / 2 + k % 2;
3974  }
3975  }
3976  double minimumBreak = start * unit;
3977  //double maximumBreak = end * unit;
3978  int count = end - start;
3979 
3980  breaks.reserve( count );
3981  for ( int i = 1; i < count + 1; i++ )
3982  {
3983  breaks.append( minimumBreak + i * unit );
3984  }
3985 
3986  if ( breaks.isEmpty() )
3987  return breaks;
3988 
3989  if ( breaks.first() < minimum )
3990  {
3991  breaks[0] = minimum;
3992  }
3993  if ( breaks.last() > maximum )
3994  {
3995  breaks[breaks.count()-1] = maximum;
3996  }
3997 
3998  return breaks;
3999 }
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
uchar * scanLine(int i)
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
static WkbType flatType(WkbType type)
Map 2d+ to 2d type.
Definition: qgis.cpp:365
static QString encodeSldLineJoinStyle(Qt::PenJoinStyle style)
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
qlonglong toLongLong(bool *ok) const
void setForceVectorOutput(bool force)
static QgsSymbolV2::OutputUnit decodeSldUom(const QString &str, double *scaleFactor)
void clear()
void setLocked(bool locked)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
QDomNodeList elementsByTagName(const QString &tagname) const
QImage convertToFormat(Format format, QFlags< Qt::ImageConversionFlag > flags) const
static QgsSymbolV2Map loadSymbols(QDomElement &element)
virtual NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
void setClipFeaturesToExtent(bool clipFeaturesToExtent)
Sets whether features drawn by the symbol should be clipped to the render context's extent...
Definition: qgssymbolv2.h:206
QString cap(int nth) const
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
QString & append(QChar ch)
static void multiplyImageOpacity(QImage *image, qreal alpha)
Multiplies opacity of image pixel values with a (global) transparency value.
QByteArray data(const QString &mimeType) const
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:55
static void drawStippledBackground(QPainter *painter, QRect rect)
static Qt::BrushStyle decodeBrushStyle(const QString &str)
int width() const
static QIcon colorRampPreviewIcon(QgsVectorColorRampV2 *ramp, QSize size)
virtual QString type() const =0
static QIcon symbolLayerPreviewIcon(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
static QPixmap colorRampPreviewPixmap(QgsVectorColorRampV2 *ramp, QSize size)
bool end()
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about 'classes' equally spaced round values which cover the range of values fr...
bool contains(const Key &key) const
static QString quotedColumnRef(QString name)
return quoted column reference (in double quotes)
static Q_DECL_DEPRECATED bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &borderColor, double &borderWidth, double &size)
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
static QMimeData * colorToMimeData(const QColor &color)
Creates mime data from a color.
void fillRect(const QRectF &rectangle, const QBrush &brush)
int localeAwareCompare(const QString &other) const
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
const QString expression() const
Alias for dump()
QString readLine(qint64 maxlen)
void append(const T &value)
void fill(const QColor &color)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
SymbolType type() const
Definition: qgssymbolv2.h:95
int right() const
QString name() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QString attribute(const QString &name, const QString &defValue) const
static double mapUnitScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> map units.
int length() const
QString nodeValue() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool hasFormat(const QString &mimeType) const
int weight() const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
void reserve(int alloc)
static QString encodeColor(const QColor &color)
QDateTime toDateTime() const
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
static QString encodeSldUom(QgsSymbolV2::OutputUnit unit, double *scaleFactor)
QString attributeNS(const QString nsURI, const QString &localName, const QString &defValue) const
QgsMultiPolyline asMultiPolyline() const
Return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
static void externalGraphicToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &mime, const QColor &color, double size=-1)
void setColorData(const QVariant &color)
static QgsStringMap getVendorOptionList(QDomElement &element)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
double computeMapUnitsPerPixel(const QgsRenderContext &c) const
Computes a map units per pixel scaling factor, respecting the minimum and maximum scales set for the ...
QgsPolygon asPolygon() const
Return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list...
const_iterator constEnd() const
The output shall be in pixels.
Definition: qgssymbolv2.h:60
Calculate scale by the diameter.
Definition: qgssymbolv2.h:81
const T & at(int i) const
QTime toTime() const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
int size() const
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
static QVector< qreal > decodeRealVector(const QString &s)
static QColor colorFromMimeData(const QMimeData *data, bool &hasAlpha)
Attempts to parse mime data as a color.
QString simplified() const
static bool functionFromSldElement(QDomElement &element, QString &function)
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
QDomElement nextSiblingElement(const QString &tagName) const
static QString encodeSldFontStyle(QFont::Style style)
T value() const
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
QgsGeometry * pointOnSurface() const
Returns a point within a geometry.
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAlpha(int alpha)
virtual QgsStringMap properties() const =0
Line symbol.
Definition: qgssymbolv2.h:71
int height() const
static QPointF decodePoint(const QString &str)
GeometryType
Definition: qgis.h:104
static bool convertPolygonSymbolizerToPointMarker(QDomElement &element, QgsSymbolLayerV2List &layerList)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QgsSymbolLayerV2Registry * instance()
return the single instance of this class (instantiate it if not exists)
static QVector< qreal > decodeSldRealVector(const QString &s)
QDomElement documentElement() const
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static Qt::BrushStyle decodeSldBrushStyle(const QString &str)
double scaleFactor() const
QString join(const QString &separator) const
bool hasText() const
bool exists() const
static void createOnlineResourceElement(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format)
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
void drawLine(const QLineF &line)
static QStringList listSvgFilesAt(const QString &directory)
Return a list of svg files at the specified directory.
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
void setRgb(int r, int g, int b, int a)
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:315
void clear()
void chop(int n)
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
bool isValidColor(const QString &name)
QDomNodeList childNodes() const
qulonglong toULongLong(bool *ok) const
static bool needMarkerLine(QDomElement &element)
QChar separator()
static bool needPointPatternFill(QDomElement &element)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
double maxScale
The maximum scale, or 0.0 if unset.
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:288
static QgsSymbolV2 * loadSymbol(const QDomElement &element)
Attempts to load a symbol from a DOM element.
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> pixel dimensions.
Marker symbol.
Definition: qgssymbolv2.h:70
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
int size() const
void setMapUnitScale(const QgsMapUnitScale &scale)
QgsMultiPolygon asMultiPolygon() const
Return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QDomNode nextSibling() const
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
QDomElement toElement() const
QList< Key > keys() const
static QString encodePenStyle(Qt::PenStyle style)
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=0)
Creates OGC filter XML element.
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
int indexIn(const QString &str, int offset, CaretMode caretMode) const
T * data()
static QString symbolPathToName(QString path)
Get symbols's name from its path.
void clear()
Mixed units in symbol layers.
Definition: qgssymbolv2.h:59
QString canonicalFilePath() const
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
static QgsRenderContext createRenderContext(QPainter *p)
Creates a render context for a pixel based device.
The output shall be in millimeters.
Definition: qgssymbolv2.h:57
static QPainter::CompositionMode decodeBlendMode(const QString &s)
QString number(int n, int base)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
qreal x() const
qreal y() const
void append(const T &value)
static QString encodeSldFontWeight(int weight)
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=0)
Create ogr feature style string for pen.
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
void setScaleFactor(double factor)
QString localName() const
uint toUInt(bool *ok) const
void resize(int size)
bool atEnd() const
QString canonicalPath() const
QString text() const
QString text() const
int toInt(bool *ok) const
QString path() const
static QgsSymbolLayerV2 * createFillLayerFromSld(QDomElement &element)
static QString encodePoint(QPointF point)
bool hasAttribute(const QString &name) const
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
static QPointF offsetPoint(QPointF pt, double angle, double dist)
static QString encodeSldAlpha(int alpha)
static bool needLinePatternFill(QDomElement &element)
static QgsPaintEffectRegistry * instance()
int top() const
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:191
int captureCount() const
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
int red() const
void setPen(const QColor &color)
int width() const
void setRenderingPass(int renderingPass)
void setAttribute(const QString &name, const QString &value)
int left() const
static QStringList listSvgFiles()
Return a list of all available svg files.
int toInt(bool *ok, int base) const
bool isEmpty() const
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
QDomNodeList elementsByTagName(const QString &tagname) const
bool isEmpty() const
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QString symbolNameToPath(QString name)
Get symbol's path from its name.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
#define M_PI
The output shall be in map unitx.
Definition: qgssymbolv2.h:58
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
QPaintDevice * device() const
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:122
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
void setText(const QString &text)
void setPainter(QPainter *p)
static bool needFontMarker(QDomElement &element)
double rasterScaleFactor() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
static void saveProperties(QgsStringMap props, QDomDocument &doc, QDomElement &element)
virtual QgsStringMap properties() const =0
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
T & first()
QVector< QgsPolyline > QgsPolygon
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:50
iterator end()
static double estimateMaxSymbolBleed(QgsSymbolV2 *symbol)
Returns the maximum estimated bleed for the symbol.
static bool hasExternalGraphic(QDomElement &element)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString scheme() const
QVariant colorData() const
int alpha() const
QgsGeometry * buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
A class to represent a point.
Definition: qgspoint.h:63
iterator begin()
static Qt::PenStyle decodePenStyle(const QString &str)
QString toLocalFile() const
virtual QColor color(double value) const =0
int logicalDpiX() const
QDomText createTextNode(const QString &value)
int green() const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
iterator end()
bool exists() const
static bool needSvgMarker(QDomElement &element)
static bool needSvgFill(QDomElement &element)
static bool geometryFromSldElement(QDomElement &element, QString &geomFunc)
static QgsSymbolLayerV2 * loadSymbolLayer(QDomElement &element)
static bool pointInPolygon(const QPolygonF &points, const QPointF &point)
Calculate whether a point is within of a QPolygonF.
bool contains(QChar ch, Qt::CaseSensitivity cs) const
static QgsSymbolLayerV2 * createLineLayerFromSld(QDomElement &element)
static QString encodeRealVector(const QVector< qreal > &v)
int renderingPass() const
virtual QString layerType() const =0
virtual void close()
QgsSymbolLayerV2 * createSymbolLayerFromSld(const QString &name, QDomElement &element) const
create a new instance of symbol layer given symbol layer name and SLD
bool isNull() const
virtual QgsSymbolV2 * subSymbol()
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
SymbolType
Type of the symbol.
Definition: qgssymbolv2.h:68
void setTexture(const QPixmap &pixmap)
int bytesPerLine() const
QgsPolyline asPolyline() const
Return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
QDate toDate() const
int blue() const
const T & at(int i) const
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
const_iterator constBegin() const
Contains information about the context of a rendering operation.
bool isValid() const
void save(QTextStream &str, int indent) const
static QDomElement createSvgParameterElement(QDomDocument &doc, const QString &name, const QString &value)
QDomNode firstChild() const
int width() const
QString mid(int position, int n) const
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static QString encodeBrushStyle(Qt::BrushStyle style)
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
static QColor parseColorWithAlpha(const QString &colorStr, bool &containsAlpha, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
static QPointF linesIntersection(QPointF p1, double t1, QPointF p2, double t2)
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:78
Struct for storing maximum and minimum scales for measurements in map units.
bool isLocked() const
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
static QList< QColor > parseColorList(const QString &colorStr)
Attempts to parse a string as a list of colors using a variety of common formats, including hex codes...
void insert(int i, const T &value)
QgsGeometry * offsetCurve(double distance, int segments, int joinStyle, double mitreLimit) const
Returns an offset line at a given distance and side from an input line.
QStringList entryList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
QString family() const
static QMimeData * colorListToMimeData(const QgsNamedColorList &colorList, const bool allFormats=true)
Creates mime data from a list of named colors.
static bool lineInfo(QPointF p1, QPointF p2, double &angle, double &t)
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void setX(qreal x)
void setY(qreal y)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:353
static QString symbolProperties(QgsSymbolV2 *symbol)
Returns a string representing the symbol.
QDomElement firstChildElement(const QString &tagName) const
static QString _nameForSymbolType(QgsSymbolV2::SymbolType type)
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
static QString encodeSldRealVector(const QVector< qreal > &v)
T & last()
void getRgb(int *r, int *g, int *b, int *a) const
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
typedef ConstIterator
static QString encodeSldBrushStyle(Qt::BrushStyle style)
int height() const
int count(const T &value) const
Fill symbol.
Definition: qgssymbolv2.h:72
QgsSymbolLayerV2 * createSymbolLayer(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
create a new instance of symbol layer given symbol layer name and properties
static void labelTextToSld(QDomDocument &doc, QDomElement &element, const QString &label, const QFont &font, const QColor &color=QColor(), double size=-1)
int bottom() const
qreal & rx()
qreal & ry()
QDomComment createComment(const QString &value)
QStringList split(const QString &sep, const QString &str, bool allowEmptyEntries)
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QPicture symbolLayerPreviewPicture(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit units, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
Draws a symbol layer preview to a QPicture.
static QgsGeometry * fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
void push_back(const T &value)
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
static void clearSymbolMap(QgsSymbolV2Map &symbols)
static QStringList svgPaths()
Returns the pathes to svg directories.
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static Qt::PenCapStyle decodeSldLineCapStyle(const QString &str)
int height() const
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=0, const Qt::PenCapStyle *penCapStyle=0, const QVector< qreal > *customDashPattern=0, double dashOffset=0.0)
double toDouble(bool *ok) const
static QColor decodeColor(const QString &str)
iterator insert(const Key &key, const T &value)
static QgsStringMap getSvgParameterList(QDomElement &element)
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
void setRasterScaleFactor(double factor)
Calculate scale by the area.
Definition: qgssymbolv2.h:80
QString tagName() const
static QgsGeometry * fromPolygon(const QgsPolygon &polygon)
Creates a new geometry from a QgsPolygon.
static int decodeSldFontWeight(const QString &str)
static int decodeSldAlpha(const QString &str)
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
void setData(const QString &mimeType, const QByteArray &data)
static bool onlineResourceFromSldElement(QDomElement &element, QString &path, QString &format)
int size() const
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
uint length() const
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
static QgsStringMap parseProperties(QDomElement &element)
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
virtual void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)=0
const_iterator constBegin() const
static bool hasWellKnownMark(QDomElement &element)
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=0, Qt::PenCapStyle *penCapStyle=0, QVector< qreal > *customDashPattern=0, double *dashOffset=0)
Type type() const
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=0, const QColor &color=QColor(), double size=-1)
static QPointF polygonPointOnSurface(const QPolygonF &points)
Calculate a point within of a QPolygonF.
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=0)
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=0)
Draw icon of the symbol that occupyies area given by size using the painter.
static Qt::PenJoinStyle decodeSldLineJoinStyle(const QString &str)
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
static QFont::Style decodeSldFontStyle(const QString &str)
int size() const
static QPointF pointOnLineWithDistance(const QPointF &startPoint, const QPointF &directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context's extent...
Definition: qgssymbolv2.h:216
bool begin(QPaintDevice *device)
double minScale
The minimum scale, or 0.0 if unset.
int compare(const QString &other) const
static bool opacityFromSldElement(QDomElement &element, QString &alphaFunc)
QString parserErrorString() const
Returns parser error.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
Format format() const
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
static bool saveColorsToGpl(QFile &file, const QString &paletteName, const QgsNamedColorList &colors)
Exports colors to a gpl GIMP palette file.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the layer.
void setOutputUnit(QgsSymbolV2::OutputUnit u)
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:193
iterator begin()
static void blurImageInPlace(QImage &image, const QRect &rect, int radius, bool alphaOnly)
Blurs an image in place, e.g.
T take(const Key &key)
QChar toChar() const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
static QString encodeSldLineCapStyle(Qt::PenCapStyle style)
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the layer.
static void premultiplyColor(QColor &rgb, int alpha)
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value.
QByteArray toByteArray(int indent) const
bool isValid() const
static void createOpacityElement(QDomDocument &doc, QDomElement &element, const QString &alphaFunc)
bool hasColor() const
static QgsNamedColorList colorListFromMimeData(const QMimeData *data)
Attempts to parse mime data as a list of named colors.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
Style style() const
const T value(const Key &key) const
static bool needEllipseMarker(QDomElement &element)
bool isNull() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &borderColor=QColor(), double borderWidth=-1, double size=-1)