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  Q_FOREACH ( const QString& name, symbols.keys() )
2796  {
2797  delete symbols.value( name );
2798  }
2799  symbols.clear();
2800 }
2801 
2802 
2804 {
2805  QString rampType = element.attribute( "type" );
2806 
2807  // parse properties
2809 
2810  if ( rampType == "gradient" )
2811  return QgsVectorGradientColorRampV2::create( props );
2812  else if ( rampType == "random" )
2813  return QgsVectorRandomColorRampV2::create( props );
2814  else if ( rampType == "colorbrewer" )
2816  else if ( rampType == "cpt-city" )
2817  return QgsCptCityColorRampV2::create( props );
2818  else
2819  {
2820  QgsDebugMsg( "unknown colorramp type " + rampType );
2821  return nullptr;
2822  }
2823 }
2824 
2825 
2827 {
2828  QDomElement rampEl = doc.createElement( "colorramp" );
2829  rampEl.setAttribute( "type", ramp->type() );
2830  rampEl.setAttribute( "name", name );
2831 
2832  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2833  return rampEl;
2834 }
2835 
2837 {
2838  if ( !color.isValid() )
2839  {
2840  return QString();
2841  }
2842 
2843  //TODO - utilise a color names database (such as X11) to return nicer names
2844  //for now, just return hex codes
2845  return color.name();
2846 }
2847 
2849 {
2850  QList<QColor> colors;
2851 
2852  //try splitting string at commas, spaces or newlines
2853  QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
2854  QStringList::iterator it = components.begin();
2855  for ( ; it != components.end(); ++it )
2856  {
2857  QColor result = parseColor( *it, true );
2858  if ( result.isValid() )
2859  {
2860  colors << result;
2861  }
2862  }
2863  if ( colors.length() > 0 )
2864  {
2865  return colors;
2866  }
2867 
2868  //try splitting string at commas or newlines
2869  components = colorStr.split( QRegExp( "(,|\n)" ) );
2870  it = components.begin();
2871  for ( ; it != components.end(); ++it )
2872  {
2873  QColor result = parseColor( *it, true );
2874  if ( result.isValid() )
2875  {
2876  colors << result;
2877  }
2878  }
2879  if ( colors.length() > 0 )
2880  {
2881  return colors;
2882  }
2883 
2884  //try splitting string at whitespace or newlines
2885  components = colorStr.simplified().split( QString( ' ' ) );
2886  it = components.begin();
2887  for ( ; it != components.end(); ++it )
2888  {
2889  QColor result = parseColor( *it, true );
2890  if ( result.isValid() )
2891  {
2892  colors << result;
2893  }
2894  }
2895  if ( colors.length() > 0 )
2896  {
2897  return colors;
2898  }
2899 
2900  //try splitting string just at newlines
2901  components = colorStr.split( '\n' );
2902  it = components.begin();
2903  for ( ; it != components.end(); ++it )
2904  {
2905  QColor result = parseColor( *it, true );
2906  if ( result.isValid() )
2907  {
2908  colors << result;
2909  }
2910  }
2911 
2912  return colors;
2913 }
2914 
2916 {
2917  //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
2918  //value, and can be used when pasting colors outside of QGIS).
2919  QMimeData *mimeData = new QMimeData;
2920  mimeData->setColorData( QVariant( color ) );
2921  mimeData->setText( color.name() );
2922  return mimeData;
2923 }
2924 
2925 QColor QgsSymbolLayerV2Utils::colorFromMimeData( const QMimeData * mimeData, bool& hasAlpha )
2926 {
2927  //attempt to read color data directly from mime
2928  QColor mimeColor = mimeData->colorData().value<QColor>();
2929  if ( mimeColor.isValid() )
2930  {
2931  hasAlpha = true;
2932  return mimeColor;
2933  }
2934 
2935  //attempt to intrepret a color from mime text data
2936  hasAlpha = false;
2937  QColor textColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( mimeData->text(), hasAlpha );
2938  if ( textColor.isValid() )
2939  {
2940  return textColor;
2941  }
2942 
2943  //could not get color from mime data
2944  return QColor();
2945 }
2946 
2948 {
2949  QgsNamedColorList mimeColors;
2950 
2951  //prefer xml format
2952  if ( data->hasFormat( "text/xml" ) )
2953  {
2954  //get XML doc
2955  QByteArray encodedData = data->data( "text/xml" );
2956  QDomDocument xmlDoc;
2957  xmlDoc.setContent( encodedData );
2958 
2959  QDomElement dragDataElem = xmlDoc.documentElement();
2960  if ( dragDataElem.tagName() == "ColorSchemeModelDragData" )
2961  {
2962  QDomNodeList nodeList = dragDataElem.childNodes();
2963  int nChildNodes = nodeList.size();
2964  QDomElement currentElem;
2965 
2966  for ( int i = 0; i < nChildNodes; ++i )
2967  {
2968  currentElem = nodeList.at( i ).toElement();
2969  if ( currentElem.isNull() )
2970  {
2971  continue;
2972  }
2973 
2974  QPair< QColor, QString> namedColor;
2975  namedColor.first = QgsSymbolLayerV2Utils::decodeColor( currentElem.attribute( "color", "255,255,255,255" ) );
2976  namedColor.second = currentElem.attribute( "label", "" );
2977 
2978  mimeColors << namedColor;
2979  }
2980  }
2981  }
2982 
2983  if ( mimeColors.length() == 0 && data->hasFormat( "application/x-colorobject-list" ) )
2984  {
2985  //get XML doc
2986  QByteArray encodedData = data->data( "application/x-colorobject-list" );
2987  QDomDocument xmlDoc;
2988  xmlDoc.setContent( encodedData );
2989 
2990  QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QString( "colors" ) );
2991  if ( colorsNodes.length() > 0 )
2992  {
2993  QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
2994  QDomNodeList colorNodeList = colorsElem.childNodes();
2995  int nChildNodes = colorNodeList.size();
2996  QDomElement currentElem;
2997 
2998  for ( int i = 0; i < nChildNodes; ++i )
2999  {
3000  //li element
3001  currentElem = colorNodeList.at( i ).toElement();
3002  if ( currentElem.isNull() )
3003  {
3004  continue;
3005  }
3006 
3007  QDomNodeList colorNodes = currentElem.elementsByTagName( QString( "color" ) );
3008  QDomNodeList nameNodes = currentElem.elementsByTagName( QString( "name" ) );
3009 
3010  if ( colorNodes.length() > 0 )
3011  {
3012  QDomElement colorElem = colorNodes.at( 0 ).toElement();
3013 
3014  QStringList colorParts = colorElem.text().simplified().split( ' ' );
3015  if ( colorParts.length() < 3 )
3016  {
3017  continue;
3018  }
3019 
3020  int red = colorParts.at( 0 ).toDouble() * 255;
3021  int green = colorParts.at( 1 ).toDouble() * 255;
3022  int blue = colorParts.at( 2 ).toDouble() * 255;
3023  QPair< QColor, QString> namedColor;
3024  namedColor.first = QColor( red, green, blue );
3025  if ( nameNodes.length() > 0 )
3026  {
3027  QDomElement nameElem = nameNodes.at( 0 ).toElement();
3028  namedColor.second = nameElem.text();
3029  }
3030  mimeColors << namedColor;
3031  }
3032  }
3033  }
3034  }
3035 
3036  if ( mimeColors.length() == 0 && data->hasText() )
3037  {
3038  //attempt to read color data from mime text
3040  QList< QColor >::iterator it = parsedColors.begin();
3041  for ( ; it != parsedColors.end(); ++it )
3042  {
3043  mimeColors << qMakePair( *it, QString() );
3044  }
3045  }
3046 
3047  if ( mimeColors.length() == 0 && data->hasColor() )
3048  {
3049  //attempt to read color data directly from mime
3050  QColor mimeColor = data->colorData().value<QColor>();
3051  if ( mimeColor.isValid() )
3052  {
3053  mimeColors << qMakePair( mimeColor, QString() );
3054  }
3055  }
3056 
3057  return mimeColors;
3058 }
3059 
3061 {
3062  //native format
3063  QMimeData* mimeData = new QMimeData();
3064  QDomDocument xmlDoc;
3065  QDomElement xmlRootElement = xmlDoc.createElement( "ColorSchemeModelDragData" );
3066  xmlDoc.appendChild( xmlRootElement );
3067 
3068  QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
3069  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3070  {
3071  QDomElement namedColor = xmlDoc.createElement( "NamedColor" );
3072  namedColor.setAttribute( "color", QgsSymbolLayerV2Utils::encodeColor(( *colorIt ).first ) );
3073  namedColor.setAttribute( "label", ( *colorIt ).second );
3074  xmlRootElement.appendChild( namedColor );
3075  }
3076  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
3077 
3078  if ( !allFormats )
3079  {
3080  return mimeData;
3081  }
3082 
3083  //set mime text to list of hex values
3084  colorIt = colorList.constBegin();
3085  QStringList colorListString;
3086  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3087  {
3088  colorListString << ( *colorIt ).first.name();
3089  }
3090  mimeData->setText( colorListString.join( "\n" ) );
3091 
3092  //set mime color data to first color
3093  if ( colorList.length() > 0 )
3094  {
3095  mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
3096  }
3097 
3098  return mimeData;
3099 }
3100 
3101 bool QgsSymbolLayerV2Utils::saveColorsToGpl( QFile &file, const QString& paletteName, const QgsNamedColorList& colors )
3102 {
3103  if ( !file.open( QIODevice::ReadWrite ) )
3104  {
3105  return false;
3106  }
3107 
3108  QTextStream stream( &file );
3109  stream << "GIMP Palette" << endl;
3110  if ( paletteName.isEmpty() )
3111  {
3112  stream << "Name: QGIS Palette" << endl;
3113  }
3114  else
3115  {
3116  stream << "Name: " << paletteName << endl;
3117  }
3118  stream << "Columns: 4" << endl;
3119  stream << '#' << endl;
3120 
3121  for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
3122  {
3123  QColor color = ( *colorIt ).first;
3124  if ( !color.isValid() )
3125  {
3126  continue;
3127  }
3128  stream << QString( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
3129  stream << "\t" << (( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
3130  }
3131  file.close();
3132 
3133  return true;
3134 }
3135 
3137 {
3138  QgsNamedColorList importedColors;
3139 
3140  if ( !file.open( QIODevice::ReadOnly ) )
3141  {
3142  ok = false;
3143  return importedColors;
3144  }
3145 
3146  QTextStream in( &file );
3147 
3148  QString line = in.readLine();
3149  if ( !line.startsWith( "GIMP Palette" ) )
3150  {
3151  ok = false;
3152  return importedColors;
3153  }
3154 
3155  //find name line
3156  while ( !in.atEnd() && !line.startsWith( "Name:" ) && !line.startsWith( '#' ) )
3157  {
3158  line = in.readLine();
3159  }
3160  if ( line.startsWith( "Name:" ) )
3161  {
3162  QRegExp nameRx( "Name:\\s*(\\S.*)$" );
3163  if ( nameRx.indexIn( line ) != -1 )
3164  {
3165  name = nameRx.cap( 1 );
3166  }
3167  }
3168 
3169  //ignore lines until after "#"
3170  while ( !in.atEnd() && !line.startsWith( '#' ) )
3171  {
3172  line = in.readLine();
3173  }
3174  if ( in.atEnd() )
3175  {
3176  ok = false;
3177  return importedColors;
3178  }
3179 
3180  //ready to start reading colors
3181  QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
3182  while ( !in.atEnd() )
3183  {
3184  line = in.readLine();
3185  if ( rx.indexIn( line ) == -1 )
3186  {
3187  continue;
3188  }
3189  int red = rx.cap( 1 ).toInt();
3190  int green = rx.cap( 2 ).toInt();
3191  int blue = rx.cap( 3 ).toInt();
3192  QColor color = QColor( red, green, blue );
3193  if ( !color.isValid() )
3194  {
3195  continue;
3196  }
3197 
3198  //try to read color name
3199  QString label;
3200  if ( rx.captureCount() > 3 )
3201  {
3202  label = rx.cap( 4 ).simplified();
3203  }
3204  else
3205  {
3206  label = colorToName( color );
3207  }
3208 
3209  importedColors << qMakePair( color, label );
3210  }
3211 
3212  file.close();
3213  ok = true;
3214  return importedColors;
3215 }
3216 
3217 QColor QgsSymbolLayerV2Utils::parseColor( const QString& colorStr, bool strictEval )
3218 {
3219  bool hasAlpha;
3220  return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
3221 }
3222 
3223 QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString& colorStr, bool &containsAlpha, bool strictEval )
3224 {
3225  QColor parsedColor;
3226 
3227  //color in hex format "#aabbcc"
3228  if ( QColor::isValidColor( colorStr ) )
3229  {
3230  //string is a valid hex color string
3231  parsedColor.setNamedColor( colorStr );
3232  if ( parsedColor.isValid() )
3233  {
3234  containsAlpha = false;
3235  return parsedColor;
3236  }
3237  }
3238 
3239  //color in hex format, with alpha
3240  QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
3241  if ( hexColorAlphaRx.indexIn( colorStr ) != -1 )
3242  {
3243  QString hexColor = hexColorAlphaRx.cap( 1 );
3244  parsedColor.setNamedColor( QString( "#" ) + hexColor );
3245  bool alphaOk;
3246  int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
3247 
3248  if ( parsedColor.isValid() && alphaOk )
3249  {
3250  parsedColor.setAlpha( alphaHex );
3251  containsAlpha = true;
3252  return parsedColor;
3253  }
3254  }
3255 
3256  if ( !strictEval )
3257  {
3258  //color in hex format, without #
3259  QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
3260  if ( hexColorRx2.indexIn( colorStr ) != -1 )
3261  {
3262  //add "#" and parse
3263  parsedColor.setNamedColor( QString( "#" ) + colorStr );
3264  if ( parsedColor.isValid() )
3265  {
3266  containsAlpha = false;
3267  return parsedColor;
3268  }
3269  }
3270  }
3271 
3272  //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
3273  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*$" );
3274  if ( rgbFormatRx.indexIn( colorStr ) != -1 )
3275  {
3276  int r = rgbFormatRx.cap( 1 ).toInt();
3277  int g = rgbFormatRx.cap( 2 ).toInt();
3278  int b = rgbFormatRx.cap( 3 ).toInt();
3279  parsedColor.setRgb( r, g, b );
3280  if ( parsedColor.isValid() )
3281  {
3282  containsAlpha = false;
3283  return parsedColor;
3284  }
3285  }
3286 
3287  //color in (r%,g%,b%) format, brackets and rgb prefix optional
3288  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*$" );
3289  if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
3290  {
3291  int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3292  int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3293  int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3294  parsedColor.setRgb( r, g, b );
3295  if ( parsedColor.isValid() )
3296  {
3297  containsAlpha = false;
3298  return parsedColor;
3299  }
3300  }
3301 
3302  //color in (r,g,b,a) format, brackets and rgba prefix optional
3303  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*$" );
3304  if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
3305  {
3306  int r = rgbaFormatRx.cap( 1 ).toInt();
3307  int g = rgbaFormatRx.cap( 2 ).toInt();
3308  int b = rgbaFormatRx.cap( 3 ).toInt();
3309  int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
3310  parsedColor.setRgb( r, g, b, a );
3311  if ( parsedColor.isValid() )
3312  {
3313  containsAlpha = true;
3314  return parsedColor;
3315  }
3316  }
3317 
3318  //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
3319  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*$" );
3320  if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
3321  {
3322  int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3323  int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3324  int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3325  int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
3326  parsedColor.setRgb( r, g, b, a );
3327  if ( parsedColor.isValid() )
3328  {
3329  containsAlpha = true;
3330  return parsedColor;
3331  }
3332  }
3333 
3334  //couldn't parse string as color
3335  return QColor();
3336 }
3337 
3339 {
3340  switch ( u )
3341  {
3342  case QgsSymbolV2::MM:
3343  return c.scaleFactor();
3344  case QgsSymbolV2::MapUnit:
3345  {
3346  double mup = scale.computeMapUnitsPerPixel( c );
3347  if ( mup > 0 )
3348  {
3349  return 1.0 / mup;
3350  }
3351  else
3352  {
3353  return 1.0;
3354  }
3355  }
3356  case QgsSymbolV2::Pixel:
3357  return 1.0 / c.rasterScaleFactor();
3358  case QgsSymbolV2::Mixed:
3360  //no sensible value
3361  return 1.0;
3362  }
3363  return 1.0;
3364 }
3365 
3367 {
3368  double conversionFactor = lineWidthScaleFactor( c, unit, scale );
3369  double convertedSize = size * conversionFactor;
3370 
3371  if ( unit == QgsSymbolV2::MapUnit )
3372  {
3373  //check max/min size
3374  if ( scale.minSizeMMEnabled )
3375  convertedSize = qMax( convertedSize, scale.minSizeMM * c.scaleFactor() );
3376  if ( scale.maxSizeMMEnabled )
3377  convertedSize = qMin( convertedSize, scale.maxSizeMM * c.scaleFactor() );
3378  }
3379 
3380  return convertedSize;
3381 }
3382 
3384 {
3385  switch ( u )
3386  {
3387  case QgsSymbolV2::MM:
3388  return ( c.scaleFactor() * c.rasterScaleFactor() );
3389  case QgsSymbolV2::MapUnit:
3390  {
3391  double mup = scale.computeMapUnitsPerPixel( c );
3392  if ( mup > 0 )
3393  {
3394  return c.rasterScaleFactor() / mup;
3395  }
3396  else
3397  {
3398  return 1.0;
3399  }
3400  }
3401  case QgsSymbolV2::Pixel:
3402  return 1.0;
3403  case QgsSymbolV2::Mixed:
3405  //no sensible value
3406  return 1.0;
3407  }
3408  return 1.0;
3409 }
3410 
3412 {
3413  switch ( u )
3414  {
3415  case QgsSymbolV2::MM:
3416  return scale.computeMapUnitsPerPixel( c ) * c.scaleFactor() * c.rasterScaleFactor();
3417  case QgsSymbolV2::MapUnit:
3418  {
3419  return 1.0;
3420  }
3421  case QgsSymbolV2::Pixel:
3422  return scale.computeMapUnitsPerPixel( c );
3423  case QgsSymbolV2::Mixed:
3425  //no sensible value
3426  return 1.0;
3427  }
3428  return 1.0;
3429 }
3430 
3432 {
3433  QgsRenderContext context;
3434  context.setPainter( p );
3435  context.setRasterScaleFactor( 1.0 );
3436  if ( p && p->device() )
3437  {
3438  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
3439  }
3440  else
3441  {
3442  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
3443  }
3444  return context;
3445 }
3446 
3448 {
3449  if ( !image )
3450  {
3451  return;
3452  }
3453 
3454  QRgb myRgb;
3455  QImage::Format format = image->format();
3456  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
3457  {
3458  QgsDebugMsg( "no alpha channel." );
3459  return;
3460  }
3461 
3462  //change the alpha component of every pixel
3463  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
3464  {
3465  QRgb* scanLine = reinterpret_cast< QRgb* >( image->scanLine( heightIndex ) );
3466  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
3467  {
3468  myRgb = scanLine[widthIndex];
3469  if ( format == QImage::Format_ARGB32_Premultiplied )
3470  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3471  else
3472  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3473  }
3474  }
3475 }
3476 
3477 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, QRect rect, int radius, bool alphaOnly )
3478 {
3479  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
3480  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
3481  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
3482 
3483  if ( image.format() != QImage::Format_ARGB32_Premultiplied
3484  && image.format() != QImage::Format_RGB32 )
3485  {
3486  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
3487  }
3488 
3489  int r1 = rect.top();
3490  int r2 = rect.bottom();
3491  int c1 = rect.left();
3492  int c2 = rect.right();
3493 
3494  int bpl = image.bytesPerLine();
3495  int rgba[4];
3496  unsigned char* p;
3497 
3498  int i1 = 0;
3499  int i2 = 3;
3500 
3501  if ( alphaOnly ) // this seems to only work right for a black color
3502  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
3503 
3504  for ( int col = c1; col <= c2; col++ )
3505  {
3506  p = image.scanLine( r1 ) + col * 4;
3507  for ( int i = i1; i <= i2; i++ )
3508  rgba[i] = p[i] << 4;
3509 
3510  p += bpl;
3511  for ( int j = r1; j < r2; j++, p += bpl )
3512  for ( int i = i1; i <= i2; i++ )
3513  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3514  }
3515 
3516  for ( int row = r1; row <= r2; row++ )
3517  {
3518  p = image.scanLine( row ) + c1 * 4;
3519  for ( int i = i1; i <= i2; i++ )
3520  rgba[i] = p[i] << 4;
3521 
3522  p += 4;
3523  for ( int j = c1; j < c2; j++, p += 4 )
3524  for ( int i = i1; i <= i2; i++ )
3525  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3526  }
3527 
3528  for ( int col = c1; col <= c2; col++ )
3529  {
3530  p = image.scanLine( r2 ) + col * 4;
3531  for ( int i = i1; i <= i2; i++ )
3532  rgba[i] = p[i] << 4;
3533 
3534  p -= bpl;
3535  for ( int j = r1; j < r2; j++, p -= bpl )
3536  for ( int i = i1; i <= i2; i++ )
3537  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3538  }
3539 
3540  for ( int row = r1; row <= r2; row++ )
3541  {
3542  p = image.scanLine( row ) + c2 * 4;
3543  for ( int i = i1; i <= i2; i++ )
3544  rgba[i] = p[i] << 4;
3545 
3546  p -= 4;
3547  for ( int j = c1; j < c2; j++, p -= 4 )
3548  for ( int i = i1; i <= i2; i++ )
3549  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3550  }
3551 }
3552 
3554 {
3555  if ( alpha != 255 && alpha > 0 )
3556  {
3557  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3558  // where color values have to be premultiplied by alpha
3559  double alphaFactor = alpha / 255.;
3560  int r = 0, g = 0, b = 0;
3561  rgb.getRgb( &r, &g, &b );
3562 
3563  r *= alphaFactor;
3564  g *= alphaFactor;
3565  b *= alphaFactor;
3566  rgb.setRgb( r, g, b, alpha );
3567  }
3568  else if ( alpha == 0 )
3569  {
3570  rgb.setRgb( 0, 0, 0, 0 );
3571  }
3572 }
3573 
3574 #if 0
3575 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
3576 {
3577  switch ( lhs.type() )
3578  {
3579  case QVariant::Int:
3580  return lhs.toInt() < rhs.toInt();
3581  case QVariant::UInt:
3582  return lhs.toUInt() < rhs.toUInt();
3583  case QVariant::LongLong:
3584  return lhs.toLongLong() < rhs.toLongLong();
3585  case QVariant::ULongLong:
3586  return lhs.toULongLong() < rhs.toULongLong();
3587  case QVariant::Double:
3588  return lhs.toDouble() < rhs.toDouble();
3589  case QVariant::Char:
3590  return lhs.toChar() < rhs.toChar();
3591  case QVariant::Date:
3592  return lhs.toDate() < rhs.toDate();
3593  case QVariant::Time:
3594  return lhs.toTime() < rhs.toTime();
3595  case QVariant::DateTime:
3596  return lhs.toDateTime() < rhs.toDateTime();
3597  default:
3598  return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
3599  }
3600 }
3601 
3602 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
3603 {
3604  return ! _QVariantLessThan( lhs, rhs );
3605 }
3606 #endif
3607 
3609 {
3610  if ( order == Qt::AscendingOrder )
3611  {
3612  //qSort( list.begin(), list.end(), _QVariantLessThan );
3613  qSort( list.begin(), list.end(), qgsVariantLessThan );
3614  }
3615  else // Qt::DescendingOrder
3616  {
3617  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
3618  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
3619  }
3620 }
3621 
3622 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance )
3623 {
3624  double dx = directionPoint.x() - startPoint.x();
3625  double dy = directionPoint.y() - startPoint.y();
3626  double length = sqrt( dx * dx + dy * dy );
3627  double scaleFactor = distance / length;
3628  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
3629 }
3630 
3631 
3633 {
3634  // copied from QgsMarkerCatalogue - TODO: unify
3635  QStringList list;
3637 
3638  for ( int i = 0; i < svgPaths.size(); i++ )
3639  {
3640  QDir dir( svgPaths[i] );
3641  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3642  {
3643  svgPaths.insert( i + 1, dir.path() + '/' + item );
3644  }
3645 
3646  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3647  {
3648  // TODO test if it is correct SVG
3649  list.append( dir.path() + '/' + item );
3650  }
3651  }
3652  return list;
3653 }
3654 
3655 // Stripped down version of listSvgFiles() for specified directory
3657 {
3658  // TODO anything that applies for the listSvgFiles() applies this also
3659 
3660  QStringList list;
3661  QStringList svgPaths;
3662  svgPaths.append( directory );
3663 
3664  for ( int i = 0; i < svgPaths.size(); i++ )
3665  {
3666  QDir dir( svgPaths[i] );
3667  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3668  {
3669  svgPaths.insert( i + 1, dir.path() + '/' + item );
3670  }
3671 
3672  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3673  {
3674  list.append( dir.path() + '/' + item );
3675  }
3676  }
3677  return list;
3678 
3679 }
3680 
3682 {
3683  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
3684 
3685  // we might have a full path...
3686  if ( QFile( name ).exists() )
3687  return QFileInfo( name ).canonicalFilePath();
3688 
3689  // or it might be an url...
3690  if ( name.contains( "://" ) )
3691  {
3692  QUrl url( name );
3693  if ( url.isValid() && !url.scheme().isEmpty() )
3694  {
3695  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
3696  {
3697  // it's a url to a local file
3698  name = url.toLocalFile();
3699  if ( QFile( name ).exists() )
3700  {
3701  return QFileInfo( name ).canonicalFilePath();
3702  }
3703  }
3704  else
3705  {
3706  // it's a url pointing to a online resource
3707  return name;
3708  }
3709  }
3710  }
3711 
3712  // SVG symbol not found - probably a relative path was used
3713 
3715  for ( int i = 0; i < svgPaths.size(); i++ )
3716  {
3717  QString svgPath = svgPaths[i];
3718  if ( svgPath.endsWith( QChar( '/' ) ) )
3719  {
3720  svgPath.chop( 1 );
3721  }
3722 
3723  QgsDebugMsg( "SvgPath: " + svgPath );
3724  // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
3725  //QFileInfo myInfo( name );
3726  //QString myFileName = myInfo.fileName(); // foo.svg
3727  //QString myLowestDir = myInfo.dir().dirName();
3728  //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : '/' + myLowestDir ) + '/' + myFileName;
3729  QString myLocalPath = svgPath + QDir::separator() + name;
3730 
3731  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
3732  if ( QFile( myLocalPath ).exists() )
3733  {
3734  QgsDebugMsg( "Svg found in alternative path" );
3735  return QFileInfo( myLocalPath ).canonicalFilePath();
3736  }
3737  }
3738 
3739  QFileInfo pfi( QgsProject::instance()->fileName() );
3740  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
3741  if ( pfi.exists() && QFile( alternatePath ).exists() )
3742  {
3743  QgsDebugMsg( "Svg found in alternative path" );
3744  return QFileInfo( alternatePath ).canonicalFilePath();
3745  }
3746  else
3747  {
3748  QgsDebugMsg( "Svg not found in project path" );
3749  }
3750  //couldnt find the file, no happy ending :-(
3751  QgsDebugMsg( "Computed alternate path but no svg there either" );
3752 
3753  return QString();
3754 }
3755 
3757 {
3758  // copied from QgsSymbol::writeXML
3759 
3760  QFileInfo fi( path );
3761  if ( !fi.exists() )
3762  return path;
3763 
3764  path = fi.canonicalFilePath();
3765 
3767 
3768  bool isInSvgPathes = false;
3769  for ( int i = 0; i < svgPaths.size(); i++ )
3770  {
3771  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
3772 
3773  if ( !dir.isEmpty() && path.startsWith( dir ) )
3774  {
3775  path = path.mid( dir.size() + 1 );
3776  isInSvgPathes = true;
3777  break;
3778  }
3779  }
3780 
3781  if ( isInSvgPathes )
3782  return path;
3783 
3784  return QgsProject::instance()->writePath( path );
3785 }
3786 
3788 {
3789  //Calculate the centroid of points
3790  double cx = 0, cy = 0;
3791  double area, sum = 0;
3792  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
3793  {
3794  const QPointF& p1 = points[i];
3795  const QPointF& p2 = points[j];
3796  area = p1.x() * p2.y() - p1.y() * p2.x();
3797  sum += area;
3798  cx += ( p1.x() + p2.x() ) * area;
3799  cy += ( p1.y() + p2.y() ) * area;
3800  }
3801  sum *= 3.0;
3802  if ( qgsDoubleNear( sum, 0.0 ) )
3803  {
3804  // the linear ring is invalid - let's fall back to a solution that will still
3805  // allow us render at least something (instead of just returning point nan,nan)
3806  if ( points.count() >= 2 )
3807  return QPointF(( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
3808  else if ( points.count() == 1 )
3809  return points[0];
3810  else
3811  return QPointF(); // hopefully we shouldn't ever get here
3812  }
3813  cx /= sum;
3814  cy /= sum;
3815 
3816  return QPointF( cx, cy );
3817 }
3818 
3820 {
3821  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3822 
3823  // check if centroid inside in polygon
3824  if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3825  {
3826  unsigned int i, pointCount = points.count();
3827 
3828  QgsPolyline polyline( pointCount );
3829  for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3830 
3831  QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3832  if ( geom )
3833  {
3834  QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3835 
3836  if ( pointOnSurfaceGeom )
3837  {
3838  QgsPoint point = pointOnSurfaceGeom->asPoint();
3839  delete pointOnSurfaceGeom;
3840  delete geom;
3841 
3842  return QPointF( point.x(), point.y() );
3843  }
3844  delete geom;
3845  }
3846  }
3847  return centroid;
3848 }
3849 
3851 {
3852  bool inside = false;
3853 
3854  double x = point.x();
3855  double y = point.y();
3856 
3857  for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3858  {
3859  const QPointF& p1 = points[i];
3860  const QPointF& p2 = points[j];
3861 
3862  if ( qgsDoubleNear( p1.x(), x ) && qgsDoubleNear( p1.y(), y ) )
3863  return true;
3864 
3865  if (( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3866  {
3867  if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3868  inside = !inside;
3869  }
3870 
3871  j = i;
3872  }
3873  return inside;
3874 }
3875 
3877 {
3878  if ( fieldOrExpression.isEmpty() )
3879  return nullptr;
3880 
3881  QgsExpression* expr = new QgsExpression( fieldOrExpression );
3882  if ( !expr->hasParserError() )
3883  return expr;
3884 
3885  // now try with quoted field name
3886  delete expr;
3887  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
3888  Q_ASSERT( !expr2->hasParserError() );
3889  return expr2;
3890 }
3891 
3893 {
3894  const QgsExpression::Node* n = expression->rootNode();
3895 
3896  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
3897  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
3898 
3899  return expression->expression();
3900 }
3901 
3902 QList<double> QgsSymbolLayerV2Utils::prettyBreaks( double minimum, double maximum, int classes )
3903 {
3904  // C++ implementation of R's pretty algorithm
3905  // Based on code for determining optimal tick placement for statistical graphics
3906  // from the R statistical programming language.
3907  // Code ported from R implementation from 'labeling' R package
3908  //
3909  // Computes a sequence of about 'classes' equally spaced round values
3910  // which cover the range of values from 'minimum' to 'maximum'.
3911  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
3912 
3913  QList<double> breaks;
3914  if ( classes < 1 )
3915  {
3916  breaks.append( maximum );
3917  return breaks;
3918  }
3919 
3920  int minimumCount = static_cast< int >( classes ) / 3;
3921  double shrink = 0.75;
3922  double highBias = 1.5;
3923  double adjustBias = 0.5 + 1.5 * highBias;
3924  int divisions = classes;
3925  double h = highBias;
3926  double cell;
3927  int U;
3928  bool small = false;
3929  double dx = maximum - minimum;
3930 
3931  if ( qgsDoubleNear( dx, 0.0 ) && qgsDoubleNear( maximum, 0.0 ) )
3932  {
3933  cell = 1.0;
3934  small = true;
3935  U = 1;
3936  }
3937  else
3938  {
3939  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
3940  if ( adjustBias >= 1.5 * h + 0.5 )
3941  {
3942  U = 1 + ( 1.0 / ( 1 + h ) );
3943  }
3944  else
3945  {
3946  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
3947  }
3948  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
3949  }
3950 
3951  if ( small )
3952  {
3953  if ( cell > 10 )
3954  {
3955  cell = 9 + cell / 10;
3956  cell = cell * shrink;
3957  }
3958  if ( minimumCount > 1 )
3959  {
3960  cell = cell / minimumCount;
3961  }
3962  }
3963  else
3964  {
3965  cell = dx;
3966  if ( divisions > 1 )
3967  {
3968  cell = cell / divisions;
3969  }
3970  }
3971  if ( cell < 20 * 1e-07 )
3972  {
3973  cell = 20 * 1e-07;
3974  }
3975 
3976  double base = pow( 10.0, floor( log10( cell ) ) );
3977  double unit = base;
3978  if (( 2 * base ) - cell < h *( cell - unit ) )
3979  {
3980  unit = 2.0 * base;
3981  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
3982  {
3983  unit = 5.0 * base;
3984  if (( 10.0 * base ) - cell < h *( cell - unit ) )
3985  {
3986  unit = 10.0 * base;
3987  }
3988  }
3989  }
3990  // Maybe used to correct for the epsilon here??
3991  int start = floor( minimum / unit + 1e-07 );
3992  int end = ceil( maximum / unit - 1e-07 );
3993 
3994  // Extend the range out beyond the data. Does this ever happen??
3995  while ( start * unit > minimum + ( 1e-07 * unit ) )
3996  {
3997  start = start - 1;
3998  }
3999  while ( end * unit < maximum - ( 1e-07 * unit ) )
4000  {
4001  end = end + 1;
4002  }
4003  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
4004 
4005  // If we don't have quite enough labels, extend the range out
4006  // to make more (these labels are beyond the data :( )
4007  int k = floor( 0.5 + end - start );
4008  if ( k < minimumCount )
4009  {
4010  k = minimumCount - k;
4011  if ( start >= 0 )
4012  {
4013  end = end + k / 2;
4014  start = start - k / 2 + k % 2;
4015  }
4016  else
4017  {
4018  start = start - k / 2;
4019  end = end + k / 2 + k % 2;
4020  }
4021  }
4022  double minimumBreak = start * unit;
4023  //double maximumBreak = end * unit;
4024  int count = end - start;
4025 
4026  breaks.reserve( count );
4027  for ( int i = 1; i < count + 1; i++ )
4028  {
4029  breaks.append( minimumBreak + i * unit );
4030  }
4031 
4032  if ( breaks.isEmpty() )
4033  return breaks;
4034 
4035  if ( breaks.first() < minimum )
4036  {
4037  breaks[0] = minimum;
4038  }
4039  if ( breaks.last() > maximum )
4040  {
4041  breaks[breaks.count()-1] = maximum;
4042  }
4043 
4044  return breaks;
4045 }
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
QList< Key > keys() 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
const T value(const Key &key) const
static bool needEllipseMarker(QDomElement &element)
bool isNull() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &borderColor=QColor(), double borderWidth=-1, double size=-1)