QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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  // check if ogc:Filter or containe ogc:Filters
2581  QDomElement elem = element;
2582  if ( element.tagName() != "Filter" )
2583  {
2584  QDomNodeList filterNodes = element.elementsByTagName( "Filter" );
2585  if ( !filterNodes.isEmpty() )
2586  {
2587  elem = filterNodes.at( 0 ).toElement();
2588  }
2589  }
2590 
2591  if ( elem.isNull() )
2592  {
2593  return false;
2594  }
2595 
2596  // parse ogc:Filter
2598  if ( !expr )
2599  return false;
2600 
2601  bool valid = !expr->hasParserError();
2602  if ( !valid )
2603  {
2604  QgsDebugMsg( "parser error: " + expr->parserErrorString() );
2605  }
2606  else
2607  {
2608  function = expr->expression();
2609  }
2610 
2611  delete expr;
2612  return valid;
2613 }
2614 
2616  const QString& path, const QString& format )
2617 {
2618  // get resource url or relative path
2619  QString url = symbolPathToName( path );
2620  QDomElement onlineResourceElem = doc.createElement( "se:OnlineResource" );
2621  onlineResourceElem.setAttribute( "xlink:type", "simple" );
2622  onlineResourceElem.setAttribute( "xlink:href", url );
2623  element.appendChild( onlineResourceElem );
2624 
2625  QDomElement formatElem = doc.createElement( "se:Format" );
2626  formatElem.appendChild( doc.createTextNode( format ) );
2627  element.appendChild( formatElem );
2628 }
2629 
2631 {
2632  QgsDebugMsg( "Entered." );
2633 
2634  QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
2635  if ( onlineResourceElem.isNull() )
2636  return false;
2637 
2638  path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
2639 
2640  QDomElement formatElem = element.firstChildElement( "Format" );
2641  if ( formatElem.isNull() )
2642  return false; // OnlineResource requires a Format sibling element
2643 
2644  format = formatElem.firstChild().nodeValue();
2645  return true;
2646 }
2647 
2648 
2650 {
2651  QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
2652  nodeElem.setAttribute( "name", name );
2653  nodeElem.appendChild( doc.createTextNode( value ) );
2654  return nodeElem;
2655 }
2656 
2658 {
2659  QgsStringMap params;
2660  QString value;
2661 
2662  QDomElement paramElem = element.firstChildElement();
2663  while ( !paramElem.isNull() )
2664  {
2665  if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
2666  {
2667  QString name = paramElem.attribute( "name" );
2668  if ( paramElem.firstChild().nodeType() == QDomNode::TextNode )
2669  {
2670  value = paramElem.firstChild().nodeValue();
2671  }
2672  else
2673  {
2674  if ( paramElem.firstChild().nodeType() == QDomNode::ElementNode &&
2675  paramElem.firstChild().localName() == "Literal" )
2676  {
2677  QgsDebugMsg( paramElem.firstChild().localName() );
2678  value = paramElem.firstChild().firstChild().nodeValue();
2679  }
2680  else
2681  {
2682  QgsDebugMsg( QString( "unexpected child of %1" ).arg( paramElem.localName() ) );
2683  }
2684  }
2685 
2686  if ( !name.isEmpty() && !value.isEmpty() )
2687  params[ name ] = value;
2688  }
2689 
2690  paramElem = paramElem.nextSiblingElement();
2691  }
2692 
2693  return params;
2694 }
2695 
2697 {
2698  QDomElement nodeElem = doc.createElement( "VendorOption" );
2699  nodeElem.setAttribute( "name", name );
2700  nodeElem.appendChild( doc.createTextNode( value ) );
2701  return nodeElem;
2702 }
2703 
2705 {
2706  QgsStringMap params;
2707 
2708  QDomElement paramElem = element.firstChildElement( "VendorOption" );
2709  while ( !paramElem.isNull() )
2710  {
2711  QString name = paramElem.attribute( "name" );
2712  QString value = paramElem.firstChild().nodeValue();
2713 
2714  if ( !name.isEmpty() && !value.isEmpty() )
2715  params[ name ] = value;
2716 
2717  paramElem = paramElem.nextSiblingElement( "VendorOption" );
2718  }
2719 
2720  return params;
2721 }
2722 
2723 
2725 {
2726  QgsStringMap props;
2727  QDomElement e = element.firstChildElement();
2728  while ( !e.isNull() )
2729  {
2730  if ( e.tagName() != "prop" )
2731  {
2732  QgsDebugMsg( "unknown tag " + e.tagName() );
2733  }
2734  else
2735  {
2736  QString propKey = e.attribute( "k" );
2737  QString propValue = e.attribute( "v" );
2738  props[propKey] = propValue;
2739  }
2740  e = e.nextSiblingElement();
2741  }
2742  return props;
2743 }
2744 
2745 
2747 {
2748  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
2749  {
2750  QDomElement propEl = doc.createElement( "prop" );
2751  propEl.setAttribute( "k", it.key() );
2752  propEl.setAttribute( "v", it.value() );
2753  element.appendChild( propEl );
2754  }
2755 }
2756 
2758 {
2759  // go through symbols one-by-one and load them
2760 
2761  QgsSymbolV2Map symbols;
2762  QDomElement e = element.firstChildElement();
2763 
2764  while ( !e.isNull() )
2765  {
2766  if ( e.tagName() == "symbol" )
2767  {
2769  if ( symbol )
2770  symbols.insert( e.attribute( "name" ), symbol );
2771  }
2772  else
2773  {
2774  QgsDebugMsg( "unknown tag: " + e.tagName() );
2775  }
2776  e = e.nextSiblingElement();
2777  }
2778 
2779 
2780  // now walk through the list of symbols and find those prefixed with @
2781  // these symbols are sub-symbols of some other symbol layers
2782  // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo"
2783  QStringList subsymbols;
2784 
2785  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2786  {
2787  if ( it.key()[0] != '@' )
2788  continue;
2789 
2790  // add to array (for deletion)
2791  subsymbols.append( it.key() );
2792 
2793  QStringList parts = it.key().split( '@' );
2794  if ( parts.count() < 3 )
2795  {
2796  QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
2797  delete it.value(); // we must delete it
2798  continue; // some invalid syntax
2799  }
2800  QString symname = parts[1];
2801  int symlayer = parts[2].toInt();
2802 
2803  if ( !symbols.contains( symname ) )
2804  {
2805  QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
2806  delete it.value(); // we must delete it
2807  continue;
2808  }
2809 
2810  QgsSymbolV2* sym = symbols[symname];
2811  if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
2812  {
2813  QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
2814  delete it.value(); // we must delete it
2815  continue;
2816  }
2817 
2818  // set subsymbol takes ownership
2819  bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
2820  if ( !res )
2821  {
2822  QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
2823  }
2824 
2825 
2826  }
2827 
2828  // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
2829  for ( int i = 0; i < subsymbols.count(); i++ )
2830  symbols.take( subsymbols[i] );
2831 
2832  return symbols;
2833 }
2834 
2836 {
2837  QDomElement symbolsElem = doc.createElement( tagName );
2838 
2839  // save symbols
2840  for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its )
2841  {
2842  QDomElement symEl = saveSymbol( its.key(), its.value(), doc );
2843  symbolsElem.appendChild( symEl );
2844  }
2845 
2846  return symbolsElem;
2847 }
2848 
2850 {
2851  qDeleteAll( symbols );
2852  symbols.clear();
2853 }
2854 
2855 
2857 {
2858  QString rampType = element.attribute( "type" );
2859 
2860  // parse properties
2862 
2863  if ( rampType == "gradient" )
2864  return QgsVectorGradientColorRampV2::create( props );
2865  else if ( rampType == "random" )
2866  return QgsVectorRandomColorRampV2::create( props );
2867  else if ( rampType == "colorbrewer" )
2869  else if ( rampType == "cpt-city" )
2870  return QgsCptCityColorRampV2::create( props );
2871  else
2872  {
2873  QgsDebugMsg( "unknown colorramp type " + rampType );
2874  return nullptr;
2875  }
2876 }
2877 
2878 
2880 {
2881  QDomElement rampEl = doc.createElement( "colorramp" );
2882  rampEl.setAttribute( "type", ramp->type() );
2883  rampEl.setAttribute( "name", name );
2884 
2885  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2886  return rampEl;
2887 }
2888 
2890 {
2891  if ( !color.isValid() )
2892  {
2893  return QString();
2894  }
2895 
2896  //TODO - utilise a color names database (such as X11) to return nicer names
2897  //for now, just return hex codes
2898  return color.name();
2899 }
2900 
2902 {
2903  QList<QColor> colors;
2904 
2905  //try splitting string at commas, spaces or newlines
2906  QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
2907  QStringList::iterator it = components.begin();
2908  for ( ; it != components.end(); ++it )
2909  {
2910  QColor result = parseColor( *it, true );
2911  if ( result.isValid() )
2912  {
2913  colors << result;
2914  }
2915  }
2916  if ( colors.length() > 0 )
2917  {
2918  return colors;
2919  }
2920 
2921  //try splitting string at commas or newlines
2922  components = colorStr.split( QRegExp( "(,|\n)" ) );
2923  it = components.begin();
2924  for ( ; it != components.end(); ++it )
2925  {
2926  QColor result = parseColor( *it, true );
2927  if ( result.isValid() )
2928  {
2929  colors << result;
2930  }
2931  }
2932  if ( colors.length() > 0 )
2933  {
2934  return colors;
2935  }
2936 
2937  //try splitting string at whitespace or newlines
2938  components = colorStr.simplified().split( QString( ' ' ) );
2939  it = components.begin();
2940  for ( ; it != components.end(); ++it )
2941  {
2942  QColor result = parseColor( *it, true );
2943  if ( result.isValid() )
2944  {
2945  colors << result;
2946  }
2947  }
2948  if ( colors.length() > 0 )
2949  {
2950  return colors;
2951  }
2952 
2953  //try splitting string just at newlines
2954  components = colorStr.split( '\n' );
2955  it = components.begin();
2956  for ( ; it != components.end(); ++it )
2957  {
2958  QColor result = parseColor( *it, true );
2959  if ( result.isValid() )
2960  {
2961  colors << result;
2962  }
2963  }
2964 
2965  return colors;
2966 }
2967 
2969 {
2970  //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
2971  //value, and can be used when pasting colors outside of QGIS).
2972  QMimeData *mimeData = new QMimeData;
2973  mimeData->setColorData( QVariant( color ) );
2974  mimeData->setText( color.name() );
2975  return mimeData;
2976 }
2977 
2978 QColor QgsSymbolLayerV2Utils::colorFromMimeData( const QMimeData * mimeData, bool& hasAlpha )
2979 {
2980  //attempt to read color data directly from mime
2981  QColor mimeColor = mimeData->colorData().value<QColor>();
2982  if ( mimeColor.isValid() )
2983  {
2984  hasAlpha = true;
2985  return mimeColor;
2986  }
2987 
2988  //attempt to intrepret a color from mime text data
2989  hasAlpha = false;
2990  QColor textColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( mimeData->text(), hasAlpha );
2991  if ( textColor.isValid() )
2992  {
2993  return textColor;
2994  }
2995 
2996  //could not get color from mime data
2997  return QColor();
2998 }
2999 
3001 {
3002  QgsNamedColorList mimeColors;
3003 
3004  //prefer xml format
3005  if ( data->hasFormat( "text/xml" ) )
3006  {
3007  //get XML doc
3008  QByteArray encodedData = data->data( "text/xml" );
3009  QDomDocument xmlDoc;
3010  xmlDoc.setContent( encodedData );
3011 
3012  QDomElement dragDataElem = xmlDoc.documentElement();
3013  if ( dragDataElem.tagName() == "ColorSchemeModelDragData" )
3014  {
3015  QDomNodeList nodeList = dragDataElem.childNodes();
3016  int nChildNodes = nodeList.size();
3017  QDomElement currentElem;
3018 
3019  for ( int i = 0; i < nChildNodes; ++i )
3020  {
3021  currentElem = nodeList.at( i ).toElement();
3022  if ( currentElem.isNull() )
3023  {
3024  continue;
3025  }
3026 
3027  QPair< QColor, QString> namedColor;
3028  namedColor.first = QgsSymbolLayerV2Utils::decodeColor( currentElem.attribute( "color", "255,255,255,255" ) );
3029  namedColor.second = currentElem.attribute( "label", "" );
3030 
3031  mimeColors << namedColor;
3032  }
3033  }
3034  }
3035 
3036  if ( mimeColors.length() == 0 && data->hasFormat( "application/x-colorobject-list" ) )
3037  {
3038  //get XML doc
3039  QByteArray encodedData = data->data( "application/x-colorobject-list" );
3040  QDomDocument xmlDoc;
3041  xmlDoc.setContent( encodedData );
3042 
3043  QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QString( "colors" ) );
3044  if ( colorsNodes.length() > 0 )
3045  {
3046  QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
3047  QDomNodeList colorNodeList = colorsElem.childNodes();
3048  int nChildNodes = colorNodeList.size();
3049  QDomElement currentElem;
3050 
3051  for ( int i = 0; i < nChildNodes; ++i )
3052  {
3053  //li element
3054  currentElem = colorNodeList.at( i ).toElement();
3055  if ( currentElem.isNull() )
3056  {
3057  continue;
3058  }
3059 
3060  QDomNodeList colorNodes = currentElem.elementsByTagName( QString( "color" ) );
3061  QDomNodeList nameNodes = currentElem.elementsByTagName( QString( "name" ) );
3062 
3063  if ( colorNodes.length() > 0 )
3064  {
3065  QDomElement colorElem = colorNodes.at( 0 ).toElement();
3066 
3067  QStringList colorParts = colorElem.text().simplified().split( ' ' );
3068  if ( colorParts.length() < 3 )
3069  {
3070  continue;
3071  }
3072 
3073  int red = colorParts.at( 0 ).toDouble() * 255;
3074  int green = colorParts.at( 1 ).toDouble() * 255;
3075  int blue = colorParts.at( 2 ).toDouble() * 255;
3076  QPair< QColor, QString> namedColor;
3077  namedColor.first = QColor( red, green, blue );
3078  if ( nameNodes.length() > 0 )
3079  {
3080  QDomElement nameElem = nameNodes.at( 0 ).toElement();
3081  namedColor.second = nameElem.text();
3082  }
3083  mimeColors << namedColor;
3084  }
3085  }
3086  }
3087  }
3088 
3089  if ( mimeColors.length() == 0 && data->hasText() )
3090  {
3091  //attempt to read color data from mime text
3093  QList< QColor >::iterator it = parsedColors.begin();
3094  for ( ; it != parsedColors.end(); ++it )
3095  {
3096  mimeColors << qMakePair( *it, QString() );
3097  }
3098  }
3099 
3100  if ( mimeColors.length() == 0 && data->hasColor() )
3101  {
3102  //attempt to read color data directly from mime
3103  QColor mimeColor = data->colorData().value<QColor>();
3104  if ( mimeColor.isValid() )
3105  {
3106  mimeColors << qMakePair( mimeColor, QString() );
3107  }
3108  }
3109 
3110  return mimeColors;
3111 }
3112 
3114 {
3115  //native format
3116  QMimeData* mimeData = new QMimeData();
3117  QDomDocument xmlDoc;
3118  QDomElement xmlRootElement = xmlDoc.createElement( "ColorSchemeModelDragData" );
3119  xmlDoc.appendChild( xmlRootElement );
3120 
3121  QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
3122  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3123  {
3124  QDomElement namedColor = xmlDoc.createElement( "NamedColor" );
3125  namedColor.setAttribute( "color", QgsSymbolLayerV2Utils::encodeColor(( *colorIt ).first ) );
3126  namedColor.setAttribute( "label", ( *colorIt ).second );
3127  xmlRootElement.appendChild( namedColor );
3128  }
3129  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
3130 
3131  if ( !allFormats )
3132  {
3133  return mimeData;
3134  }
3135 
3136  //set mime text to list of hex values
3137  colorIt = colorList.constBegin();
3138  QStringList colorListString;
3139  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3140  {
3141  colorListString << ( *colorIt ).first.name();
3142  }
3143  mimeData->setText( colorListString.join( "\n" ) );
3144 
3145  //set mime color data to first color
3146  if ( colorList.length() > 0 )
3147  {
3148  mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
3149  }
3150 
3151  return mimeData;
3152 }
3153 
3154 bool QgsSymbolLayerV2Utils::saveColorsToGpl( QFile &file, const QString& paletteName, const QgsNamedColorList& colors )
3155 {
3156  if ( !file.open( QIODevice::ReadWrite ) )
3157  {
3158  return false;
3159  }
3160 
3161  QTextStream stream( &file );
3162  stream << "GIMP Palette" << endl;
3163  if ( paletteName.isEmpty() )
3164  {
3165  stream << "Name: QGIS Palette" << endl;
3166  }
3167  else
3168  {
3169  stream << "Name: " << paletteName << endl;
3170  }
3171  stream << "Columns: 4" << endl;
3172  stream << '#' << endl;
3173 
3174  for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
3175  {
3176  QColor color = ( *colorIt ).first;
3177  if ( !color.isValid() )
3178  {
3179  continue;
3180  }
3181  stream << QString( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
3182  stream << "\t" << (( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
3183  }
3184  file.close();
3185 
3186  return true;
3187 }
3188 
3190 {
3191  QgsNamedColorList importedColors;
3192 
3193  if ( !file.open( QIODevice::ReadOnly ) )
3194  {
3195  ok = false;
3196  return importedColors;
3197  }
3198 
3199  QTextStream in( &file );
3200 
3201  QString line = in.readLine();
3202  if ( !line.startsWith( "GIMP Palette" ) )
3203  {
3204  ok = false;
3205  return importedColors;
3206  }
3207 
3208  //find name line
3209  while ( !in.atEnd() && !line.startsWith( "Name:" ) && !line.startsWith( '#' ) )
3210  {
3211  line = in.readLine();
3212  }
3213  if ( line.startsWith( "Name:" ) )
3214  {
3215  QRegExp nameRx( "Name:\\s*(\\S.*)$" );
3216  if ( nameRx.indexIn( line ) != -1 )
3217  {
3218  name = nameRx.cap( 1 );
3219  }
3220  }
3221 
3222  //ignore lines until after "#"
3223  while ( !in.atEnd() && !line.startsWith( '#' ) )
3224  {
3225  line = in.readLine();
3226  }
3227  if ( in.atEnd() )
3228  {
3229  ok = false;
3230  return importedColors;
3231  }
3232 
3233  //ready to start reading colors
3234  QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
3235  while ( !in.atEnd() )
3236  {
3237  line = in.readLine();
3238  if ( rx.indexIn( line ) == -1 )
3239  {
3240  continue;
3241  }
3242  int red = rx.cap( 1 ).toInt();
3243  int green = rx.cap( 2 ).toInt();
3244  int blue = rx.cap( 3 ).toInt();
3245  QColor color = QColor( red, green, blue );
3246  if ( !color.isValid() )
3247  {
3248  continue;
3249  }
3250 
3251  //try to read color name
3252  QString label;
3253  if ( rx.captureCount() > 3 )
3254  {
3255  label = rx.cap( 4 ).simplified();
3256  }
3257  else
3258  {
3259  label = colorToName( color );
3260  }
3261 
3262  importedColors << qMakePair( color, label );
3263  }
3264 
3265  file.close();
3266  ok = true;
3267  return importedColors;
3268 }
3269 
3270 QColor QgsSymbolLayerV2Utils::parseColor( const QString& colorStr, bool strictEval )
3271 {
3272  bool hasAlpha;
3273  return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
3274 }
3275 
3276 QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString& colorStr, bool &containsAlpha, bool strictEval )
3277 {
3278  QColor parsedColor;
3279 
3280  QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
3281  int hexColorIndex = hexColorAlphaRx.indexIn( colorStr );
3282 
3283  //color in hex format "#aabbcc", but not #aabbccdd
3284  if ( hexColorIndex == -1 && QColor::isValidColor( colorStr ) )
3285  {
3286  //string is a valid hex color string
3287  parsedColor.setNamedColor( colorStr );
3288  if ( parsedColor.isValid() )
3289  {
3290  containsAlpha = false;
3291  return parsedColor;
3292  }
3293  }
3294 
3295  //color in hex format, with alpha
3296  if ( hexColorIndex > -1 )
3297  {
3298  QString hexColor = hexColorAlphaRx.cap( 1 );
3299  parsedColor.setNamedColor( QString( "#" ) + hexColor );
3300  bool alphaOk;
3301  int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
3302 
3303  if ( parsedColor.isValid() && alphaOk )
3304  {
3305  parsedColor.setAlpha( alphaHex );
3306  containsAlpha = true;
3307  return parsedColor;
3308  }
3309  }
3310 
3311  if ( !strictEval )
3312  {
3313  //color in hex format, without #
3314  QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
3315  if ( hexColorRx2.indexIn( colorStr ) != -1 )
3316  {
3317  //add "#" and parse
3318  parsedColor.setNamedColor( QString( "#" ) + colorStr );
3319  if ( parsedColor.isValid() )
3320  {
3321  containsAlpha = false;
3322  return parsedColor;
3323  }
3324  }
3325  }
3326 
3327  //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
3328  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*$" );
3329  if ( rgbFormatRx.indexIn( colorStr ) != -1 )
3330  {
3331  int r = rgbFormatRx.cap( 1 ).toInt();
3332  int g = rgbFormatRx.cap( 2 ).toInt();
3333  int b = rgbFormatRx.cap( 3 ).toInt();
3334  parsedColor.setRgb( r, g, b );
3335  if ( parsedColor.isValid() )
3336  {
3337  containsAlpha = false;
3338  return parsedColor;
3339  }
3340  }
3341 
3342  //color in (r%,g%,b%) format, brackets and rgb prefix optional
3343  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*$" );
3344  if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
3345  {
3346  int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3347  int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3348  int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3349  parsedColor.setRgb( r, g, b );
3350  if ( parsedColor.isValid() )
3351  {
3352  containsAlpha = false;
3353  return parsedColor;
3354  }
3355  }
3356 
3357  //color in (r,g,b,a) format, brackets and rgba prefix optional
3358  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*$" );
3359  if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
3360  {
3361  int r = rgbaFormatRx.cap( 1 ).toInt();
3362  int g = rgbaFormatRx.cap( 2 ).toInt();
3363  int b = rgbaFormatRx.cap( 3 ).toInt();
3364  int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
3365  parsedColor.setRgb( r, g, b, a );
3366  if ( parsedColor.isValid() )
3367  {
3368  containsAlpha = true;
3369  return parsedColor;
3370  }
3371  }
3372 
3373  //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
3374  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*$" );
3375  if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
3376  {
3377  int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3378  int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3379  int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3380  int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
3381  parsedColor.setRgb( r, g, b, a );
3382  if ( parsedColor.isValid() )
3383  {
3384  containsAlpha = true;
3385  return parsedColor;
3386  }
3387  }
3388 
3389  //couldn't parse string as color
3390  return QColor();
3391 }
3392 
3394 {
3395  switch ( u )
3396  {
3397  case QgsSymbolV2::MM:
3398  return c.scaleFactor();
3399  case QgsSymbolV2::MapUnit:
3400  {
3401  double mup = scale.computeMapUnitsPerPixel( c );
3402  if ( mup > 0 )
3403  {
3404  return 1.0 / mup;
3405  }
3406  else
3407  {
3408  return 1.0;
3409  }
3410  }
3411  case QgsSymbolV2::Pixel:
3412  return 1.0 / c.rasterScaleFactor();
3413  case QgsSymbolV2::Mixed:
3415  //no sensible value
3416  return 1.0;
3417  }
3418  return 1.0;
3419 }
3420 
3422 {
3423  double conversionFactor = lineWidthScaleFactor( c, unit, scale );
3424  double convertedSize = size * conversionFactor;
3425 
3426  if ( unit == QgsSymbolV2::MapUnit )
3427  {
3428  //check max/min size
3429  if ( scale.minSizeMMEnabled )
3430  convertedSize = qMax( convertedSize, scale.minSizeMM * c.scaleFactor() );
3431  if ( scale.maxSizeMMEnabled )
3432  convertedSize = qMin( convertedSize, scale.maxSizeMM * c.scaleFactor() );
3433  }
3434 
3435  return convertedSize;
3436 }
3437 
3439 {
3440  double mup = c.mapToPixel().mapUnitsPerPixel();
3441 
3442  switch ( unit )
3443  {
3444  case QgsSymbolV2::MapUnit:
3445  {
3446  // check scale
3447  double minSizeMU = -DBL_MAX;
3448  if ( scale.minSizeMMEnabled )
3449  {
3450  minSizeMU = scale.minSizeMM * c.scaleFactor() * c.rasterScaleFactor() * mup;
3451  }
3452  if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
3453  {
3454  minSizeMU = qMax( minSizeMU, size * ( scale.minScale * c.rendererScale() ) );
3455  }
3456  size = qMax( size, minSizeMU );
3457 
3458  double maxSizeMU = DBL_MAX;
3459  if ( scale.maxSizeMMEnabled )
3460  {
3461  maxSizeMU = scale.maxSizeMM * c.scaleFactor() * c.rasterScaleFactor() * mup;
3462  }
3463  if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
3464  {
3465  maxSizeMU = qMin( maxSizeMU, size * ( scale.maxScale * c.rendererScale() ) );
3466  }
3467  size = qMin( size, maxSizeMU );
3468 
3469  return size;
3470  }
3471  case QgsSymbolV2::MM:
3472  {
3473  return size * c.scaleFactor() * c.rasterScaleFactor() * mup;
3474  }
3475  case QgsSymbolV2::Pixel:
3476  {
3477  return size * mup;
3478  }
3479 
3480  case QgsSymbolV2::Mixed:
3482  //no sensible value
3483  return 0.0;
3484  }
3485  return 0.0;
3486 }
3487 
3489 {
3490  switch ( u )
3491  {
3492  case QgsSymbolV2::MM:
3493  return ( c.scaleFactor() * c.rasterScaleFactor() );
3494  case QgsSymbolV2::MapUnit:
3495  {
3496  double mup = scale.computeMapUnitsPerPixel( c );
3497  if ( mup > 0 )
3498  {
3499  return c.rasterScaleFactor() / mup;
3500  }
3501  else
3502  {
3503  return 1.0;
3504  }
3505  }
3506  case QgsSymbolV2::Pixel:
3507  return 1.0;
3508  case QgsSymbolV2::Mixed:
3510  //no sensible value
3511  return 1.0;
3512  }
3513  return 1.0;
3514 }
3515 
3517 {
3518  switch ( u )
3519  {
3520  case QgsSymbolV2::MM:
3521  return scale.computeMapUnitsPerPixel( c ) * c.scaleFactor() * c.rasterScaleFactor();
3522  case QgsSymbolV2::MapUnit:
3523  {
3524  return 1.0;
3525  }
3526  case QgsSymbolV2::Pixel:
3527  return scale.computeMapUnitsPerPixel( c );
3528  case QgsSymbolV2::Mixed:
3530  //no sensible value
3531  return 1.0;
3532  }
3533  return 1.0;
3534 }
3535 
3537 {
3538  QgsRenderContext context;
3539  context.setPainter( p );
3540  context.setRasterScaleFactor( 1.0 );
3541  if ( p && p->device() )
3542  {
3543  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
3544  }
3545  else
3546  {
3547  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
3548  }
3549  return context;
3550 }
3551 
3553 {
3554  if ( !image )
3555  {
3556  return;
3557  }
3558 
3559  QRgb myRgb;
3560  QImage::Format format = image->format();
3561  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
3562  {
3563  QgsDebugMsg( "no alpha channel." );
3564  return;
3565  }
3566 
3567  //change the alpha component of every pixel
3568  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
3569  {
3570  QRgb* scanLine = reinterpret_cast< QRgb* >( image->scanLine( heightIndex ) );
3571  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
3572  {
3573  myRgb = scanLine[widthIndex];
3574  if ( format == QImage::Format_ARGB32_Premultiplied )
3575  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3576  else
3577  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3578  }
3579  }
3580 }
3581 
3582 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, QRect rect, int radius, bool alphaOnly )
3583 {
3584  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
3585  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
3586  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
3587 
3588  if ( image.format() != QImage::Format_ARGB32_Premultiplied
3589  && image.format() != QImage::Format_RGB32 )
3590  {
3591  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
3592  }
3593 
3594  int r1 = rect.top();
3595  int r2 = rect.bottom();
3596  int c1 = rect.left();
3597  int c2 = rect.right();
3598 
3599  int bpl = image.bytesPerLine();
3600  int rgba[4];
3601  unsigned char* p;
3602 
3603  int i1 = 0;
3604  int i2 = 3;
3605 
3606  if ( alphaOnly ) // this seems to only work right for a black color
3607  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
3608 
3609  for ( int col = c1; col <= c2; col++ )
3610  {
3611  p = image.scanLine( r1 ) + col * 4;
3612  for ( int i = i1; i <= i2; i++ )
3613  rgba[i] = p[i] << 4;
3614 
3615  p += bpl;
3616  for ( int j = r1; j < r2; j++, p += bpl )
3617  for ( int i = i1; i <= i2; i++ )
3618  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3619  }
3620 
3621  for ( int row = r1; row <= r2; row++ )
3622  {
3623  p = image.scanLine( row ) + c1 * 4;
3624  for ( int i = i1; i <= i2; i++ )
3625  rgba[i] = p[i] << 4;
3626 
3627  p += 4;
3628  for ( int j = c1; j < c2; j++, p += 4 )
3629  for ( int i = i1; i <= i2; i++ )
3630  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3631  }
3632 
3633  for ( int col = c1; col <= c2; col++ )
3634  {
3635  p = image.scanLine( r2 ) + col * 4;
3636  for ( int i = i1; i <= i2; i++ )
3637  rgba[i] = p[i] << 4;
3638 
3639  p -= bpl;
3640  for ( int j = r1; j < r2; j++, p -= bpl )
3641  for ( int i = i1; i <= i2; i++ )
3642  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3643  }
3644 
3645  for ( int row = r1; row <= r2; row++ )
3646  {
3647  p = image.scanLine( row ) + c2 * 4;
3648  for ( int i = i1; i <= i2; i++ )
3649  rgba[i] = p[i] << 4;
3650 
3651  p -= 4;
3652  for ( int j = c1; j < c2; j++, p -= 4 )
3653  for ( int i = i1; i <= i2; i++ )
3654  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3655  }
3656 }
3657 
3659 {
3660  if ( alpha != 255 && alpha > 0 )
3661  {
3662  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3663  // where color values have to be premultiplied by alpha
3664  double alphaFactor = alpha / 255.;
3665  int r = 0, g = 0, b = 0;
3666  rgb.getRgb( &r, &g, &b );
3667 
3668  r *= alphaFactor;
3669  g *= alphaFactor;
3670  b *= alphaFactor;
3671  rgb.setRgb( r, g, b, alpha );
3672  }
3673  else if ( alpha == 0 )
3674  {
3675  rgb.setRgb( 0, 0, 0, 0 );
3676  }
3677 }
3678 
3680 {
3681  if ( order == Qt::AscendingOrder )
3682  {
3683  //qSort( list.begin(), list.end(), _QVariantLessThan );
3684  qSort( list.begin(), list.end(), qgsVariantLessThan );
3685  }
3686  else // Qt::DescendingOrder
3687  {
3688  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
3689  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
3690  }
3691 }
3692 
3693 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance )
3694 {
3695  double dx = directionPoint.x() - startPoint.x();
3696  double dy = directionPoint.y() - startPoint.y();
3697  double length = sqrt( dx * dx + dy * dy );
3698  double scaleFactor = distance / length;
3699  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
3700 }
3701 
3702 
3704 {
3705  // copied from QgsMarkerCatalogue - TODO: unify
3706  QStringList list;
3708 
3709  for ( int i = 0; i < svgPaths.size(); i++ )
3710  {
3711  QDir dir( svgPaths[i] );
3712  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3713  {
3714  svgPaths.insert( i + 1, dir.path() + '/' + item );
3715  }
3716 
3717  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3718  {
3719  // TODO test if it is correct SVG
3720  list.append( dir.path() + '/' + item );
3721  }
3722  }
3723  return list;
3724 }
3725 
3726 // Stripped down version of listSvgFiles() for specified directory
3728 {
3729  // TODO anything that applies for the listSvgFiles() applies this also
3730 
3731  QStringList list;
3732  QStringList svgPaths;
3733  svgPaths.append( directory );
3734 
3735  for ( int i = 0; i < svgPaths.size(); i++ )
3736  {
3737  QDir dir( svgPaths[i] );
3738  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3739  {
3740  svgPaths.insert( i + 1, dir.path() + '/' + item );
3741  }
3742 
3743  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3744  {
3745  list.append( dir.path() + '/' + item );
3746  }
3747  }
3748  return list;
3749 
3750 }
3751 
3753 {
3754  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
3755 
3756  // we might have a full path...
3757  if ( QFile( name ).exists() )
3758  return QFileInfo( name ).canonicalFilePath();
3759 
3760  // or it might be an url...
3761  if ( name.contains( "://" ) )
3762  {
3763  QUrl url( name );
3764  if ( url.isValid() && !url.scheme().isEmpty() )
3765  {
3766  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
3767  {
3768  // it's a url to a local file
3769  name = url.toLocalFile();
3770  if ( QFile( name ).exists() )
3771  {
3772  return QFileInfo( name ).canonicalFilePath();
3773  }
3774  }
3775  else
3776  {
3777  // it's a url pointing to a online resource
3778  return name;
3779  }
3780  }
3781  }
3782 
3783  // SVG symbol not found - probably a relative path was used
3784 
3786  for ( int i = 0; i < svgPaths.size(); i++ )
3787  {
3788  QString svgPath = svgPaths[i];
3789  if ( svgPath.endsWith( QChar( '/' ) ) )
3790  {
3791  svgPath.chop( 1 );
3792  }
3793 
3794  QgsDebugMsg( "SvgPath: " + svgPath );
3795  // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
3796  //QFileInfo myInfo( name );
3797  //QString myFileName = myInfo.fileName(); // foo.svg
3798  //QString myLowestDir = myInfo.dir().dirName();
3799  //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : '/' + myLowestDir ) + '/' + myFileName;
3800  QString myLocalPath = svgPath + QDir::separator() + name;
3801 
3802  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
3803  if ( QFile( myLocalPath ).exists() )
3804  {
3805  QgsDebugMsg( "Svg found in alternative path" );
3806  return QFileInfo( myLocalPath ).canonicalFilePath();
3807  }
3808  }
3809 
3810  QString alternatePath = QgsProject::instance()->readPath( name );
3811  if ( QFile( alternatePath ).exists() )
3812  {
3813  QgsDebugMsg( "Svg found in alternative path" );
3814  return QFileInfo( alternatePath ).canonicalFilePath();
3815  }
3816  else
3817  {
3818  QgsDebugMsg( "Svg not found in project path" );
3819  }
3820  //couldnt find the file, no happy ending :-(
3821  QgsDebugMsg( "Computed alternate path but no svg there either" );
3822 
3823  return QString();
3824 }
3825 
3827 {
3828  // copied from QgsSymbol::writeXML
3829 
3830  QFileInfo fi( path );
3831  if ( !fi.exists() )
3832  return path;
3833 
3834  path = fi.canonicalFilePath();
3835 
3837 
3838  bool isInSvgPathes = false;
3839  for ( int i = 0; i < svgPaths.size(); i++ )
3840  {
3841  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
3842 
3843  if ( !dir.isEmpty() && path.startsWith( dir ) )
3844  {
3845  path = path.mid( dir.size() + 1 );
3846  isInSvgPathes = true;
3847  break;
3848  }
3849  }
3850 
3851  if ( isInSvgPathes )
3852  return path;
3853 
3854  return QgsProject::instance()->writePath( path );
3855 }
3856 
3858 {
3859  //Calculate the centroid of points
3860  double cx = 0, cy = 0;
3861  double area, sum = 0;
3862  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
3863  {
3864  const QPointF& p1 = points[i];
3865  const QPointF& p2 = points[j];
3866  area = p1.x() * p2.y() - p1.y() * p2.x();
3867  sum += area;
3868  cx += ( p1.x() + p2.x() ) * area;
3869  cy += ( p1.y() + p2.y() ) * area;
3870  }
3871  sum *= 3.0;
3872  if ( qgsDoubleNear( sum, 0.0 ) )
3873  {
3874  // the linear ring is invalid - let's fall back to a solution that will still
3875  // allow us render at least something (instead of just returning point nan,nan)
3876  if ( points.count() >= 2 )
3877  return QPointF(( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
3878  else if ( points.count() == 1 )
3879  return points[0];
3880  else
3881  return QPointF(); // hopefully we shouldn't ever get here
3882  }
3883  cx /= sum;
3884  cy /= sum;
3885 
3886  return QPointF( cx, cy );
3887 }
3888 
3890 {
3891  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3892 
3893  // check if centroid inside in polygon
3894  if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3895  {
3896  unsigned int i, pointCount = points.count();
3897 
3898  QgsPolyline polyline( pointCount );
3899  for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3900 
3901  QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3902  if ( geom )
3903  {
3904  QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3905 
3906  if ( pointOnSurfaceGeom )
3907  {
3908  QgsPoint point = pointOnSurfaceGeom->asPoint();
3909  delete pointOnSurfaceGeom;
3910  delete geom;
3911 
3912  return QPointF( point.x(), point.y() );
3913  }
3914  delete geom;
3915  }
3916  }
3917  return centroid;
3918 }
3919 
3921 {
3922  bool inside = false;
3923 
3924  double x = point.x();
3925  double y = point.y();
3926 
3927  for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3928  {
3929  const QPointF& p1 = points[i];
3930  const QPointF& p2 = points[j];
3931 
3932  if ( qgsDoubleNear( p1.x(), x ) && qgsDoubleNear( p1.y(), y ) )
3933  return true;
3934 
3935  if (( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3936  {
3937  if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3938  inside = !inside;
3939  }
3940 
3941  j = i;
3942  }
3943  return inside;
3944 }
3945 
3947 {
3948  if ( fieldOrExpression.isEmpty() )
3949  return nullptr;
3950 
3951  QgsExpression* expr = new QgsExpression( fieldOrExpression );
3952  if ( !expr->hasParserError() )
3953  return expr;
3954 
3955  // now try with quoted field name
3956  delete expr;
3957  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
3958  Q_ASSERT( !expr2->hasParserError() );
3959  return expr2;
3960 }
3961 
3963 {
3964  const QgsExpression::Node* n = expression->rootNode();
3965 
3966  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
3967  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
3968 
3969  return expression->expression();
3970 }
3971 
3972 QList<double> QgsSymbolLayerV2Utils::prettyBreaks( double minimum, double maximum, int classes )
3973 {
3974  // C++ implementation of R's pretty algorithm
3975  // Based on code for determining optimal tick placement for statistical graphics
3976  // from the R statistical programming language.
3977  // Code ported from R implementation from 'labeling' R package
3978  //
3979  // Computes a sequence of about 'classes' equally spaced round values
3980  // which cover the range of values from 'minimum' to 'maximum'.
3981  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
3982 
3983  QList<double> breaks;
3984  if ( classes < 1 )
3985  {
3986  breaks.append( maximum );
3987  return breaks;
3988  }
3989 
3990  int minimumCount = static_cast< int >( classes ) / 3;
3991  double shrink = 0.75;
3992  double highBias = 1.5;
3993  double adjustBias = 0.5 + 1.5 * highBias;
3994  int divisions = classes;
3995  double h = highBias;
3996  double cell;
3997  int U;
3998  bool small = false;
3999  double dx = maximum - minimum;
4000 
4001  if ( qgsDoubleNear( dx, 0.0 ) && qgsDoubleNear( maximum, 0.0 ) )
4002  {
4003  cell = 1.0;
4004  small = true;
4005  U = 1;
4006  }
4007  else
4008  {
4009  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
4010  if ( adjustBias >= 1.5 * h + 0.5 )
4011  {
4012  U = 1 + ( 1.0 / ( 1 + h ) );
4013  }
4014  else
4015  {
4016  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
4017  }
4018  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
4019  }
4020 
4021  if ( small )
4022  {
4023  if ( cell > 10 )
4024  {
4025  cell = 9 + cell / 10;
4026  cell = cell * shrink;
4027  }
4028  if ( minimumCount > 1 )
4029  {
4030  cell = cell / minimumCount;
4031  }
4032  }
4033  else
4034  {
4035  cell = dx;
4036  if ( divisions > 1 )
4037  {
4038  cell = cell / divisions;
4039  }
4040  }
4041  if ( cell < 20 * 1e-07 )
4042  {
4043  cell = 20 * 1e-07;
4044  }
4045 
4046  double base = pow( 10.0, floor( log10( cell ) ) );
4047  double unit = base;
4048  if (( 2 * base ) - cell < h *( cell - unit ) )
4049  {
4050  unit = 2.0 * base;
4051  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
4052  {
4053  unit = 5.0 * base;
4054  if (( 10.0 * base ) - cell < h *( cell - unit ) )
4055  {
4056  unit = 10.0 * base;
4057  }
4058  }
4059  }
4060  // Maybe used to correct for the epsilon here??
4061  int start = floor( minimum / unit + 1e-07 );
4062  int end = ceil( maximum / unit - 1e-07 );
4063 
4064  // Extend the range out beyond the data. Does this ever happen??
4065  while ( start * unit > minimum + ( 1e-07 * unit ) )
4066  {
4067  start = start - 1;
4068  }
4069  while ( end * unit < maximum - ( 1e-07 * unit ) )
4070  {
4071  end = end + 1;
4072  }
4073  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
4074 
4075  // If we don't have quite enough labels, extend the range out
4076  // to make more (these labels are beyond the data :( )
4077  int k = floor( 0.5 + end - start );
4078  if ( k < minimumCount )
4079  {
4080  k = minimumCount - k;
4081  if ( start >= 0 )
4082  {
4083  end = end + k / 2;
4084  start = start - k / 2 + k % 2;
4085  }
4086  else
4087  {
4088  start = start - k / 2;
4089  end = end + k / 2 + k % 2;
4090  }
4091  }
4092  double minimumBreak = start * unit;
4093  //double maximumBreak = end * unit;
4094  int count = end - start;
4095 
4096  breaks.reserve( count );
4097  for ( int i = 1; i < count + 1; i++ )
4098  {
4099  breaks.append( minimumBreak + i * unit );
4100  }
4101 
4102  if ( breaks.isEmpty() )
4103  return breaks;
4104 
4105  if ( breaks.first() < minimum )
4106  {
4107  breaks[0] = minimum;
4108  }
4109  if ( breaks.last() > maximum )
4110  {
4111  breaks[breaks.count()-1] = maximum;
4112  }
4113 
4114  return breaks;
4115 }
4116 
4118 {
4119  double scale = 1;
4120  bool roundToUnit = false;
4121  if ( unit == QgsSymbolV2::Mixed )
4122  {
4123  if ( props.contains( "uomScale" ) )
4124  {
4125  bool ok;
4126  scale = props.value( "uomScale" ).toDouble( &ok );
4127  if ( !ok )
4128  {
4129  return size;
4130  }
4131  }
4132  }
4133  else
4134  {
4135  if ( props.value( "uom" ) == "http://www.opengeospatial.org/se/units/metre" )
4136  {
4137  switch ( unit )
4138  {
4139  case QgsSymbolV2::MM:
4140  scale = 0.001;
4141  break;
4142  case QgsSymbolV2::Pixel:
4143  scale = 0.00028;
4144  roundToUnit = true;
4145  break;
4146  default:
4147  scale = 1;
4148  }
4149  }
4150  else
4151  {
4152  // target is pixels
4153  switch ( unit )
4154  {
4155  case QgsSymbolV2::MM:
4156  scale = 1 / 0.28;
4157  roundToUnit = true;
4158  break;
4159  // we don't have a good case for map units, as pixel values won't change based on zoom
4160  default:
4161  scale = 1;
4162  }
4163  }
4164 
4165  }
4166  double rescaled = size * scale;
4167  // round to unit if the result is pixels to avoid a weird looking SLD (people often think
4168  // of pixels as integers, even if SLD allows for float values in there
4169  if ( roundToUnit )
4170  {
4171  rescaled = qRound( rescaled );
4172  }
4173  return rescaled;
4174 }
4175 
4177 {
4178  double x = rescaleUom( point.x(), unit, props );
4179  double y = rescaleUom( point.y(), unit, props );
4180  return QPointF( x, y );
4181 }
4182 
4184 {
4185  QVector<qreal> result;
4187  for ( ; it != array.constEnd(); ++it )
4188  {
4189  result.append( rescaleUom( *it, unit, props ) );
4190  }
4191  return result;
4192 }
4193 
4195 {
4196  if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
4197  {
4198  QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
4199  scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
4200  ruleElem.appendChild( scaleMinDenomElem );
4201  }
4202 
4203  if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
4204  {
4205  QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
4206  scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
4207  ruleElem.appendChild( scaleMaxDenomElem );
4208  }
4209 }
4210 
4211 void QgsSymbolLayerV2Utils::mergeScaleDependencies( int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap& props )
4212 {
4213  if ( mScaleMinDenom != 0 )
4214  {
4215  bool ok;
4216  int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
4217  if ( !ok || parentScaleMinDenom <= 0 )
4218  props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
4219  else
4220  props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
4221  }
4222 
4223  if ( mScaleMaxDenom != 0 )
4224  {
4225  bool ok;
4226  int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
4227  if ( !ok || parentScaleMaxDenom <= 0 )
4228  props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
4229  else
4230  props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
4231  }
4232 }
4233 
4235 {
4236  double scale = 1.0;
4237 
4238  if ( uom == QLatin1String( "http://www.opengeospatial.org/se/units/metre" ) )
4239  {
4240  scale = 1.0 / 0.00028; // from meters to pixels
4241  }
4242  else if ( uom == QLatin1String( "http://www.opengeospatial.org/se/units/foot" ) )
4243  {
4244  scale = 304.8 / 0.28; // from feet to pixels
4245  }
4246  else
4247  {
4248  scale = 1.0; // from pixels to pixels (default unit)
4249  }
4250 
4251  return size * scale;
4252 }
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
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
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.
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)
NodeType nodeType() const
static Qt::BrushStyle decodeSldBrushStyle(const QString &str)
QString join(const QString &separator) const
bool hasText() 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
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)
QString readPath(QString filename, const QString &relativeBasePath=QString()) const
Turn filename read from the project file to an absolute path.
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)
QString writePath(const QString &filename, const QString &relativeBasePath=QString()) const
Prepare a filename to save it to the project file.
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.