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