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