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