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