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