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