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