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