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