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