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