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