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