QGIS API Documentation  2.14.11-Essen
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  switch ( unit )
412  {
413  case QgsSymbolV2::MM:
414  return "MM";
416  return "MapUnit";
417  case QgsSymbolV2::Pixel:
418  return "Pixel";
420  return "Percentage";
421  default:
422  return "MM";
423  }
424 }
425 
427 {
428  QString normalized = string.trimmed().toLower();
429 
430  if ( normalized == encodeOutputUnit( QgsSymbolV2::MM ).toLower() )
431  return QgsSymbolV2::MM;
432  if ( normalized == encodeOutputUnit( QgsSymbolV2::MapUnit ).toLower() )
433  return QgsSymbolV2::MapUnit;
434  if ( normalized == encodeOutputUnit( QgsSymbolV2::Pixel ).toLower() )
435  return QgsSymbolV2::Pixel;
436  if ( normalized == encodeOutputUnit( QgsSymbolV2::Percentage ).toLower() )
438 
439  // millimeters are default
440  return QgsSymbolV2::MM;
441 }
442 
444 {
445  switch ( unit )
446  {
448  if ( scaleFactor )
449  *scaleFactor = 0.001; // from millimeters to meters
450  return "http://www.opengeospatial.org/se/units/metre";
451 
452  case QgsSymbolV2::MM:
453  default:
454  // pixel is the SLD default uom. The "standardized rendering pixel
455  // size" is defined to be 0.28mm × 0.28mm (millimeters).
456  if ( scaleFactor )
457  *scaleFactor = 1 / 0.28; // from millimeters to pixels
458 
459  // http://www.opengeospatial.org/sld/units/pixel
460  return QString();
461  }
462 }
463 
465 {
466  if ( str == "http://www.opengeospatial.org/se/units/metre" )
467  {
468  if ( scaleFactor )
469  *scaleFactor = 1000.0; // from meters to millimeters
470  return QgsSymbolV2::MapUnit;
471  }
472  else if ( str == "http://www.opengeospatial.org/se/units/foot" )
473  {
474  if ( scaleFactor )
475  *scaleFactor = 304.8; // from feet to meters
476  return QgsSymbolV2::MapUnit;
477  }
478 
479  // pixel is the SLD default uom. The "standardized rendering pixel
480  // size" is defined to be 0.28mm x 0.28mm (millimeters).
481  if ( scaleFactor )
482  *scaleFactor = 1 / 0.00028; // from pixels to millimeters
483  return QgsSymbolV2::MM;
484 }
485 
487 {
488  QString vectorString;
490  for ( ; it != v.constEnd(); ++it )
491  {
492  if ( it != v.constBegin() )
493  {
494  vectorString.append( ';' );
495  }
496  vectorString.append( QString::number( *it ) );
497  }
498  return vectorString;
499 }
500 
502 {
503  QVector<qreal> resultVector;
504 
505  QStringList realList = s.split( ';' );
506  QStringList::const_iterator it = realList.constBegin();
507  for ( ; it != realList.constEnd(); ++it )
508  {
509  resultVector.append( it->toDouble() );
510  }
511 
512  return resultVector;
513 }
514 
516 {
517  QString vectorString;
519  for ( ; it != v.constEnd(); ++it )
520  {
521  if ( it != v.constBegin() )
522  {
523  vectorString.append( ' ' );
524  }
525  vectorString.append( QString::number( *it ) );
526  }
527  return vectorString;
528 }
529 
531 {
532  QVector<qreal> resultVector;
533 
534  QStringList realList = s.split( ' ' );
535  QStringList::const_iterator it = realList.constBegin();
536  for ( ; it != realList.constEnd(); ++it )
537  {
538  resultVector.append( it->toDouble() );
539  }
540 
541  return resultVector;
542 }
543 
545 {
546  QString encodedValue;
547 
548  switch ( scaleMethod )
549  {
551  encodedValue = "diameter";
552  break;
554  encodedValue = "area";
555  break;
556  }
557  return encodedValue;
558 }
559 
561 {
562  QgsSymbolV2::ScaleMethod scaleMethod;
563 
564  if ( str == "diameter" )
565  {
566  scaleMethod = QgsSymbolV2::ScaleDiameter;
567  }
568  else
569  {
570  scaleMethod = QgsSymbolV2::ScaleArea;
571  }
572 
573  return scaleMethod;
574 }
575 
576 QPainter::CompositionMode QgsSymbolLayerV2Utils::decodeBlendMode( const QString &s )
577 {
578  if ( s.compare( "Lighten", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Lighten;
579  if ( s.compare( "Screen", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Screen;
580  if ( s.compare( "Dodge", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorDodge;
581  if ( s.compare( "Addition", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Plus;
582  if ( s.compare( "Darken", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Darken;
583  if ( s.compare( "Multiply", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Multiply;
584  if ( s.compare( "Burn", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorBurn;
585  if ( s.compare( "Overlay", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Overlay;
586  if ( s.compare( "SoftLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_SoftLight;
587  if ( s.compare( "HardLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_HardLight;
588  if ( s.compare( "Difference", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Difference;
589  if ( s.compare( "Subtract", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Exclusion;
590  return QPainter::CompositionMode_SourceOver; // "Normal"
591 }
592 
594 {
595  return QIcon( symbolPreviewPixmap( symbol, size ) );
596 }
597 
599 {
600  Q_ASSERT( symbol );
601 
602  QPixmap pixmap( size );
603  pixmap.fill( Qt::transparent );
604  QPainter painter;
605  painter.begin( &pixmap );
606  painter.setRenderHint( QPainter::Antialiasing );
607  if ( customContext )
608  customContext->setPainter( &painter );
609  symbol->drawPreviewIcon( &painter, size, customContext );
610  painter.end();
611  return pixmap;
612 }
613 
615 {
616  double maxBleed = 0;
617  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
618  {
619  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
620  double layerMaxBleed = layer->estimateMaxBleed();
621  maxBleed = layerMaxBleed > maxBleed ? layerMaxBleed : maxBleed;
622  }
623 
624  return maxBleed;
625 }
626 
628 {
629  QPicture picture;
630  QPainter painter;
631  painter.begin( &picture );
632  painter.setRenderHint( QPainter::Antialiasing );
633  QgsRenderContext renderContext = createRenderContext( &painter );
634  renderContext.setForceVectorOutput( true );
635  QgsSymbolV2RenderContext symbolContext( renderContext, units, 1.0, false, 0, nullptr, nullptr, scale );
636  layer->drawPreviewIcon( symbolContext, size );
637  painter.end();
638  return picture;
639 }
640 
642 {
643  QPixmap pixmap( size );
644  pixmap.fill( Qt::transparent );
645  QPainter painter;
646  painter.begin( &pixmap );
647  painter.setRenderHint( QPainter::Antialiasing );
648  QgsRenderContext renderContext = createRenderContext( &painter );
649  QgsSymbolV2RenderContext symbolContext( renderContext, u, 1.0, false, 0, nullptr, nullptr, scale );
650  layer->drawPreviewIcon( symbolContext, size );
651  painter.end();
652  return QIcon( pixmap );
653 }
654 
656 {
657  return QIcon( colorRampPreviewPixmap( ramp, size ) );
658 }
659 
661 {
662  QPixmap pixmap( size );
663  pixmap.fill( Qt::transparent );
664  // pixmap.fill( Qt::white ); // this makes the background white instead of transparent
665  QPainter painter;
666  painter.begin( &pixmap );
667 
668  //draw stippled background, for transparent images
669  drawStippledBackground( &painter, QRect( 0, 0, size.width(), size.height() ) );
670 
671  // antialising makes the colors duller, and no point in antialiasing a color ramp
672  // painter.setRenderHint( QPainter::Antialiasing );
673  for ( int i = 0; i < size.width(); i++ )
674  {
675  QPen pen( ramp->color( static_cast< double >( i ) / size.width() ) );
676  painter.setPen( pen );
677  painter.drawLine( i, 0, i, size.height() - 1 );
678  }
679  painter.end();
680  return pixmap;
681 }
682 
684 {
685  // create a 2x2 checker-board image
686  uchar pixDataRGB[] = { 255, 255, 255, 255,
687  127, 127, 127, 255,
688  127, 127, 127, 255,
689  255, 255, 255, 255
690  };
691  QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
692  // scale it to rect so at least 5 patterns are shown
693  int width = ( rect.width() < rect.height() ) ?
694  rect.width() / 2.5 : rect.height() / 2.5;
695  QPixmap pix = QPixmap::fromImage( img.scaled( width, width ) );
696  // fill rect with texture
697  QBrush brush;
698  brush.setTexture( pix );
699  painter->fillRect( rect, brush );
700 }
701 
702 #include <QPolygonF>
703 
704 #include <cmath>
705 #include <cfloat>
706 
707 
708 #if !defined(GEOS_VERSION_MAJOR) || !defined(GEOS_VERSION_MINOR) || \
709  ((GEOS_VERSION_MAJOR<3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR<3)))
710 // calculate line's angle and tangent
711 static bool lineInfo( QPointF p1, QPointF p2, double& angle, double& t )
712 {
713  double x1 = p1.x(), y1 = p1.y(), x2 = p2.x(), y2 = p2.y();
714 
715  if ( x1 == x2 && y1 == y2 )
716  return false;
717 
718  // tangent
719  t = ( x1 == x2 ? DBL_MAX : ( y2 - y1 ) / ( x2 - x1 ) );
720 
721  // angle
722  if ( t == DBL_MAX )
723  angle = ( y2 > y1 ? M_PI / 2 : M_PI * 3 / 2 ); // angle is 90 or 270
724  else if ( t == 0 )
725  angle = ( x2 > x1 ? 0 : M_PI ); // angle is 0 or 180
726  else if ( t >= 0 )
727  angle = ( y2 > y1 ? atan( t ) : M_PI + atan( t ) );
728  else // t < 0
729  angle = ( y2 > y1 ? M_PI + atan( t ) : atan( t ) );
730 
731  return true;
732 }
733 
734 // offset a point with an angle and distance
735 static QPointF offsetPoint( QPointF pt, double angle, double dist )
736 {
737  return QPointF( pt.x() + dist * cos( angle ), pt.y() + dist * sin( angle ) );
738 }
739 
740 // calc intersection of two (infinite) lines defined by one point and tangent
741 static QPointF linesIntersection( QPointF p1, double t1, QPointF p2, double t2 )
742 {
743  // parallel lines? (or the difference between angles is less than appr. 10 degree)
744  if (( t1 == DBL_MAX && t2 == DBL_MAX ) || qAbs( atan( t1 ) - atan( t2 ) ) < 0.175 )
745  return QPointF();
746 
747  double x, y;
748  if ( t1 == DBL_MAX || t2 == DBL_MAX )
749  {
750  // in case one line is with angle 90 resp. 270 degrees (tangent undefined)
751  // swap them so that line 2 is with undefined tangent
752  if ( t1 == DBL_MAX )
753  {
754  QPointF pSwp = p1;
755  p1 = p2;
756  p2 = pSwp;
757  double tSwp = t1;
758  t1 = t2;
759  t2 = tSwp;
760  }
761 
762  x = p2.x();
763  }
764  else
765  {
766  // usual case
767  x = (( p1.y() - p2.y() ) + t2 * p2.x() - t1 * p1.x() ) / ( t2 - t1 );
768  }
769 
770  y = p1.y() + t1 * ( x - p1.x() );
771  return QPointF( x, y );
772 }
773 #else
774 static QPolygonF makeOffsetGeometry( const QgsPolyline& polyline )
775 {
776  int i, pointCount = polyline.count();
777 
778  QPolygonF resultLine;
779  resultLine.resize( pointCount );
780 
781  const QgsPoint* tempPtr = polyline.data();
782 
783  for ( i = 0; i < pointCount; ++i, tempPtr++ )
784  resultLine[i] = QPointF( tempPtr->x(), tempPtr->y() );
785 
786  return resultLine;
787 }
788 static QList<QPolygonF> makeOffsetGeometry( const QgsPolygon& polygon )
789 {
790  QList<QPolygonF> resultGeom;
791  resultGeom.reserve( polygon.size() );
792  for ( int ring = 0; ring < polygon.size(); ++ring )
793  resultGeom.append( makeOffsetGeometry( polygon[ ring ] ) );
794  return resultGeom;
795 }
796 #endif
797 
798 QList<QPolygonF> offsetLine( QPolygonF polyline, double dist, QGis::GeometryType geometryType )
799 {
800  QList<QPolygonF> resultLine;
801 
802  if ( polyline.count() < 2 )
803  {
804  resultLine.append( polyline );
805  return resultLine;
806  }
807 
808  QPolygonF newLine;
809 
810  // need at least geos 3.3 for OffsetCurve tool
811 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
812  ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
813 
814  unsigned int i, pointCount = polyline.count();
815 
816  QgsPolyline tempPolyline( pointCount );
817  QPointF* tempPtr = polyline.data();
818  for ( i = 0; i < pointCount; ++i, tempPtr++ )
819  tempPolyline[i] = QgsPoint( tempPtr->rx(), tempPtr->ry() );
820 
821  QgsGeometry* tempGeometry = geometryType == QGis::Polygon ? QgsGeometry::fromPolygon( QgsPolygon() << tempPolyline ) : QgsGeometry::fromPolyline( tempPolyline );
822  if ( tempGeometry )
823  {
824  int quadSegments = 0; // we want mitre joins, not round joins
825  double mitreLimit = 2.0; // the default value in GEOS (5.0) allows for fairly sharp endings
826  QgsGeometry* offsetGeom = nullptr;
827  if ( geometryType == QGis::Polygon )
828  offsetGeom = tempGeometry->buffer( -dist, quadSegments, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, mitreLimit );
829  else
830  offsetGeom = tempGeometry->offsetCurve( dist, quadSegments, GEOSBUF_JOIN_MITRE, mitreLimit );
831 
832  if ( offsetGeom )
833  {
834  delete tempGeometry;
835  tempGeometry = offsetGeom;
836 
837  if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBLineString )
838  {
839  QgsPolyline line = tempGeometry->asPolyline();
840  // Reverse the line if offset was negative, see
841  // http://hub.qgis.org/issues/13811
842  if ( dist < 0 ) std::reverse( line.begin(), line.end() );
843  resultLine.append( makeOffsetGeometry( line ) );
844  delete tempGeometry;
845  return resultLine;
846  }
847  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBPolygon )
848  {
849  resultLine.append( makeOffsetGeometry( tempGeometry->asPolygon() ) );
850  delete tempGeometry;
851  return resultLine;
852  }
853  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBMultiLineString )
854  {
855  QgsMultiPolyline tempMPolyline = tempGeometry->asMultiPolyline();
856  resultLine.reserve( tempMPolyline.count() );
857  for ( int part = 0; part < tempMPolyline.count(); ++part )
858  {
859  resultLine.append( makeOffsetGeometry( tempMPolyline[ part ] ) );
860  }
861  delete tempGeometry;
862  return resultLine;
863  }
864  else if ( QGis::flatType( tempGeometry->wkbType() ) == QGis::WKBMultiPolygon )
865  {
866  QgsMultiPolygon tempMPolygon = tempGeometry->asMultiPolygon();
867  resultLine.reserve( tempMPolygon.count() );
868  for ( int part = 0; part < tempMPolygon.count(); ++part )
869  {
870  resultLine.append( makeOffsetGeometry( tempMPolygon[ part ] ) );
871  }
872  delete tempGeometry;
873  return resultLine;
874  }
875  }
876  delete tempGeometry;
877  }
878 
879  // returns original polyline when 'GEOSOffsetCurve' fails!
880  resultLine.append( polyline );
881  return resultLine;
882 
883 #else
884 
885  double angle = 0.0, t_new, t_old = 0;
886  QPointF pt_old, pt_new;
887  QPointF p1 = polyline[0], p2;
888  bool first_point = true;
889 
890  for ( int i = 1; i < polyline.count(); i++ )
891  {
892  p2 = polyline[i];
893 
894  if ( !lineInfo( p1, p2, angle, t_new ) )
895  continue; // not a line...
896 
897  pt_new = offsetPoint( p1, angle + M_PI / 2, dist );
898 
899  if ( ! first_point )
900  {
901  // if it's not the first line segment
902  // calc intersection with last line (with offset)
903  QPointF pt_tmp = linesIntersection( pt_old, t_old, pt_new, t_new );
904  if ( !pt_tmp.isNull() )
905  pt_new = pt_tmp;
906  }
907 
908  newLine.append( pt_new );
909 
910  pt_old = pt_new;
911  t_old = t_new;
912  p1 = p2;
913  first_point = false;
914  }
915 
916  // last line segment:
917  pt_new = offsetPoint( p2, angle + M_PI / 2, dist );
918  newLine.append( pt_new );
919 
920  resultLine.append( newLine );
921  return resultLine;
922 
923 #endif
924 }
925 
926 QList<QPolygonF> offsetLine( const QPolygonF& polyline, double dist )
927 {
928  QGis::GeometryType geometryType = QGis::Point;
929  int pointCount = polyline.count();
930 
931  if ( pointCount > 3 && qgsDoubleNear( polyline[ 0 ].x(), polyline[ pointCount - 1 ].x() ) && qgsDoubleNear( polyline[ 0 ].y(), polyline[ pointCount - 1 ].y() ) )
932  {
933  geometryType = QGis::Polygon;
934  }
935  else if ( pointCount > 1 )
936  {
937  geometryType = QGis::Line;
938  }
939  return offsetLine( polyline, dist, geometryType );
940 }
941 
943 
944 
946 {
947  QgsSymbolLayerV2List layers;
948  QDomNode layerNode = element.firstChild();
949 
950  while ( !layerNode.isNull() )
951  {
952  QDomElement e = layerNode.toElement();
953  if ( !e.isNull() )
954  {
955  if ( e.tagName() != "layer" )
956  {
957  QgsDebugMsg( "unknown tag " + e.tagName() );
958  }
959  else
960  {
961  QgsSymbolLayerV2* layer = loadSymbolLayer( e );
962 
963  if ( layer )
964  {
965  // Dealing with sub-symbols nested into a layer
966  QDomElement s = e.firstChildElement( "symbol" );
967  if ( !s.isNull() )
968  {
969  QgsSymbolV2* subSymbol = loadSymbol( s );
970  bool res = layer->setSubSymbol( subSymbol );
971  if ( !res )
972  {
973  QgsDebugMsg( "symbol layer refused subsymbol: " + s.attribute( "name" ) );
974  }
975  }
976  layers.append( layer );
977  }
978  }
979  }
980  layerNode = layerNode.nextSibling();
981  }
982 
983  if ( layers.isEmpty() )
984  {
985  QgsDebugMsg( "no layers for symbol" );
986  return nullptr;
987  }
988 
989  QString symbolType = element.attribute( "type" );
990 
991  QgsSymbolV2* symbol = nullptr;
992  if ( symbolType == "line" )
993  symbol = new QgsLineSymbolV2( layers );
994  else if ( symbolType == "fill" )
995  symbol = new QgsFillSymbolV2( layers );
996  else if ( symbolType == "marker" )
997  symbol = new QgsMarkerSymbolV2( layers );
998  else
999  {
1000  QgsDebugMsg( "unknown symbol type " + symbolType );
1001  return nullptr;
1002  }
1003 
1004  if ( element.hasAttribute( "outputUnit" ) )
1005  {
1006  symbol->setOutputUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( element.attribute( "outputUnit" ) ) );
1007  }
1008  if ( element.hasAttribute(( "mapUnitScale" ) ) )
1009  {
1010  QgsMapUnitScale mapUnitScale;
1011  mapUnitScale.minScale = element.attribute( "mapUnitMinScale", "0.0" ).toDouble();
1012  mapUnitScale.maxScale = element.attribute( "mapUnitMaxScale", "0.0" ).toDouble();
1013  symbol->setMapUnitScale( mapUnitScale );
1014  }
1015  symbol->setAlpha( element.attribute( "alpha", "1.0" ).toDouble() );
1016  symbol->setClipFeaturesToExtent( element.attribute( "clip_to_extent", "1" ).toInt() );
1017 
1018  return symbol;
1019 }
1020 
1022 {
1023  QString layerClass = element.attribute( "class" );
1024  bool locked = element.attribute( "locked" ).toInt();
1025  int pass = element.attribute( "pass" ).toInt();
1026 
1027  // parse properties
1028  QgsStringMap props = parseProperties( element );
1029 
1030  QgsSymbolLayerV2* layer;
1031  layer = QgsSymbolLayerV2Registry::instance()->createSymbolLayer( layerClass, props );
1032  if ( layer )
1033  {
1034  layer->setLocked( locked );
1035  layer->setRenderingPass( pass );
1036 
1037  //restore layer effect
1038  QDomElement effectElem = element.firstChildElement( "effect" );
1039  if ( !effectElem.isNull() )
1040  {
1041  layer->setPaintEffect( QgsPaintEffectRegistry::instance()->createEffect( effectElem ) );
1042  }
1043  return layer;
1044  }
1045  else
1046  {
1047  QgsDebugMsg( "unknown class " + layerClass );
1048  return nullptr;
1049  }
1050 }
1051 
1053 {
1054  switch ( type )
1055  {
1056  case QgsSymbolV2::Line:
1057  return "line";
1058  case QgsSymbolV2::Marker:
1059  return "marker";
1060  case QgsSymbolV2::Fill:
1061  return "fill";
1062  default:
1063  return "";
1064  }
1065 }
1066 
1068 {
1069  Q_ASSERT( symbol );
1070  QDomElement symEl = doc.createElement( "symbol" );
1071  symEl.setAttribute( "type", _nameForSymbolType( symbol->type() ) );
1072  symEl.setAttribute( "name", name );
1073  symEl.setAttribute( "alpha", QString::number( symbol->alpha() ) );
1074  symEl.setAttribute( "clip_to_extent", symbol->clipFeaturesToExtent() ? "1" : "0" );
1075  //QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) );
1076 
1077  for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
1078  {
1079  QgsSymbolLayerV2* layer = symbol->symbolLayer( i );
1080 
1081  QDomElement layerEl = doc.createElement( "layer" );
1082  layerEl.setAttribute( "class", layer->layerType() );
1083  layerEl.setAttribute( "locked", layer->isLocked() );
1084  layerEl.setAttribute( "pass", layer->renderingPass() );
1085  saveProperties( layer->properties(), doc, layerEl );
1087  layer->paintEffect()->saveProperties( doc, layerEl );
1088 
1089  if ( layer->subSymbol() )
1090  {
1091  QString subname = QString( "@%1@%2" ).arg( name ).arg( i );
1092  QDomElement subEl = saveSymbol( subname, layer->subSymbol(), doc );
1093  layerEl.appendChild( subEl );
1094  }
1095  symEl.appendChild( layerEl );
1096  }
1097 
1098  return symEl;
1099 }
1100 
1102 {
1103  QDomDocument doc( "qgis-symbol-definition" );
1104  QDomElement symbolElem = saveSymbol( "symbol", symbol, doc );
1105  QString props;
1106  QTextStream stream( &props );
1107  symbolElem.save( stream, -1 );
1108  return props;
1109 }
1110 
1112  QGis::GeometryType geomType,
1113  QgsSymbolLayerV2List &layers )
1114 {
1115  QgsDebugMsg( "Entered." );
1116 
1117  if ( element.isNull() )
1118  return false;
1119 
1120  QgsSymbolLayerV2 *l = nullptr;
1121 
1122  QString symbolizerName = element.localName();
1123 
1124  if ( symbolizerName == "PointSymbolizer" )
1125  {
1126  // first check for Graphic element, nothing will be rendered if not found
1127  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1128  if ( graphicElem.isNull() )
1129  {
1130  QgsDebugMsg( "Graphic element not found in PointSymbolizer" );
1131  }
1132  else
1133  {
1134  switch ( geomType )
1135  {
1136  case QGis::Polygon:
1137  // polygon layer and point symbolizer: draw poligon centroid
1138  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "CentroidFill", element );
1139  if ( l )
1140  layers.append( l );
1141 
1142  break;
1143 
1144  case QGis::Point:
1145  // point layer and point symbolizer: use markers
1146  l = createMarkerLayerFromSld( element );
1147  if ( l )
1148  layers.append( l );
1149 
1150  break;
1151 
1152  case QGis::Line:
1153  // line layer and point symbolizer: draw central point
1154  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
1155  if ( l )
1156  layers.append( l );
1157 
1158  break;
1159 
1160  default:
1161  break;
1162  }
1163  }
1164  }
1165 
1166  if ( symbolizerName == "LineSymbolizer" )
1167  {
1168  // check for Stroke element, nothing will be rendered if not found
1169  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1170  if ( strokeElem.isNull() )
1171  {
1172  QgsDebugMsg( "Stroke element not found in LineSymbolizer" );
1173  }
1174  else
1175  {
1176  switch ( geomType )
1177  {
1178  case QGis::Polygon:
1179  case QGis::Line:
1180  // polygon layer and line symbolizer: draw polygon outline
1181  // line layer and line symbolizer: draw line
1182  l = createLineLayerFromSld( element );
1183  if ( l )
1184  layers.append( l );
1185 
1186  break;
1187 
1188  case QGis::Point:
1189  // point layer and line symbolizer: draw a little line marker
1190  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
1191  if ( l )
1192  layers.append( l );
1193 
1194  break;
1195 
1196  default:
1197  break;
1198  }
1199  }
1200  }
1201 
1202  if ( symbolizerName == "PolygonSymbolizer" )
1203  {
1204  // get Fill and Stroke elements, nothing will be rendered if both are missing
1205  QDomElement fillElem = element.firstChildElement( "Fill" );
1206  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1207  if ( fillElem.isNull() && strokeElem.isNull() )
1208  {
1209  QgsDebugMsg( "neither Fill nor Stroke element not found in PolygonSymbolizer" );
1210  }
1211  else
1212  {
1213  QgsSymbolLayerV2 *l = nullptr;
1214 
1215  switch ( geomType )
1216  {
1217  case QGis::Polygon:
1218  // polygon layer and polygon symbolizer: draw fill
1219 
1220  l = createFillLayerFromSld( element );
1221  if ( l )
1222  {
1223  layers.append( l );
1224 
1225  // SVGFill and SimpleFill symbolLayerV2 supports outline internally,
1226  // so don't go forward to create a different symbolLayerV2 for outline
1227  if ( l->layerType() == "SimpleFill" || l->layerType() == "SVGFill" )
1228  break;
1229  }
1230 
1231  // now create polygon outline
1232  // polygon layer and polygon symbolizer: draw polygon outline
1233  l = createLineLayerFromSld( element );
1234  if ( l )
1235  layers.append( l );
1236 
1237  break;
1238 
1239  case QGis::Line:
1240  // line layer and polygon symbolizer: draw line
1241  l = createLineLayerFromSld( element );
1242  if ( l )
1243  layers.append( l );
1244 
1245  break;
1246 
1247  case QGis::Point:
1248  // point layer and polygon symbolizer: draw a square marker
1249  convertPolygonSymbolizerToPointMarker( element, layers );
1250  break;
1251 
1252  default:
1253  break;
1254  }
1255  }
1256  }
1257 
1258  return true;
1259 }
1260 
1262 {
1263  QDomElement fillElem = element.firstChildElement( "Fill" );
1264  if ( fillElem.isNull() )
1265  {
1266  QgsDebugMsg( "Fill element not found" );
1267  return nullptr;
1268  }
1269 
1270  QgsSymbolLayerV2 *l = nullptr;
1271 
1272  if ( needLinePatternFill( element ) )
1273  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "LinePatternFill", element );
1274  else if ( needPointPatternFill( element ) )
1275  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "PointPatternFill", element );
1276  else if ( needSvgFill( element ) )
1277  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SVGFill", element );
1278  else
1279  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleFill", element );
1280 
1281  return l;
1282 }
1283 
1285 {
1286  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1287  if ( strokeElem.isNull() )
1288  {
1289  QgsDebugMsg( "Stroke element not found" );
1290  return nullptr;
1291  }
1292 
1293  QgsSymbolLayerV2 *l = nullptr;
1294 
1295  if ( needMarkerLine( element ) )
1296  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "MarkerLine", element );
1297  else
1298  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleLine", element );
1299 
1300  return l;
1301 }
1302 
1304 {
1305  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1306  if ( graphicElem.isNull() )
1307  {
1308  QgsDebugMsg( "Graphic element not found" );
1309  return nullptr;
1310  }
1311 
1312  QgsSymbolLayerV2 *l = nullptr;
1313 
1314  if ( needFontMarker( element ) )
1315  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "FontMarker", element );
1316  else if ( needSvgMarker( element ) )
1317  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SvgMarker", element );
1318  else if ( needEllipseMarker( element ) )
1319  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "EllipseMarker", element );
1320  else
1321  l = QgsSymbolLayerV2Registry::instance()->createSymbolLayerFromSld( "SimpleMarker", element );
1322 
1323  return l;
1324 }
1325 
1327 {
1328  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1329  if ( graphicElem.isNull() )
1330  return false;
1331 
1332  QDomElement externalGraphicElem = graphicElem.firstChildElement( "ExternalGraphic" );
1333  if ( externalGraphicElem.isNull() )
1334  return false;
1335 
1336  // check for format
1337  QDomElement formatElem = externalGraphicElem.firstChildElement( "Format" );
1338  if ( formatElem.isNull() )
1339  return false;
1340 
1341  QString format = formatElem.firstChild().nodeValue();
1342  if ( format != "image/svg+xml" )
1343  {
1344  QgsDebugMsg( "unsupported External Graphic format found: " + format );
1345  return false;
1346  }
1347 
1348  // check for a valid content
1349  QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( "OnlineResource" );
1350  QDomElement inlineContentElem = externalGraphicElem.firstChildElement( "InlineContent" );
1351  if ( !onlineResourceElem.isNull() )
1352  {
1353  return true;
1354  }
1355 #if 0
1356  else if ( !inlineContentElem.isNull() )
1357  {
1358  return false; // not implemented yet
1359  }
1360 #endif
1361  else
1362  {
1363  return false;
1364  }
1365 }
1366 
1368 {
1369  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1370  if ( graphicElem.isNull() )
1371  return false;
1372 
1373  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1374  if ( markElem.isNull() )
1375  return false;
1376 
1377  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
1378  if ( wellKnownNameElem.isNull() )
1379  return false;
1380 
1381  return true;
1382 }
1383 
1384 
1386 {
1387  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1388  if ( graphicElem.isNull() )
1389  return false;
1390 
1391  QDomElement markElem = graphicElem.firstChildElement( "Mark" );
1392  if ( markElem.isNull() )
1393  return false;
1394 
1395  // check for format
1396  QDomElement formatElem = markElem.firstChildElement( "Format" );
1397  if ( formatElem.isNull() )
1398  return false;
1399 
1400  QString format = formatElem.firstChild().nodeValue();
1401  if ( format != "ttf" )
1402  {
1403  QgsDebugMsg( "unsupported Graphic Mark format found: " + format );
1404  return false;
1405  }
1406 
1407  // check for a valid content
1408  QDomElement onlineResourceElem = markElem.firstChildElement( "OnlineResource" );
1409  QDomElement inlineContentElem = markElem.firstChildElement( "InlineContent" );
1410  if ( !onlineResourceElem.isNull() )
1411  {
1412  // mark with ttf format has a markIndex element
1413  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
1414  if ( !markIndexElem.isNull() )
1415  return true;
1416  }
1417  else if ( !inlineContentElem.isNull() )
1418  {
1419  return false; // not implemented yet
1420  }
1421 
1422  return false;
1423 }
1424 
1426 {
1427  return hasExternalGraphic( element );
1428 }
1429 
1431 {
1432  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1433  if ( graphicElem.isNull() )
1434  return false;
1435 
1436  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
1437  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1438  {
1439  if ( it.key() == "widthHeightFactor" )
1440  {
1441  return true;
1442  }
1443  }
1444 
1445  return false;
1446 }
1447 
1449 {
1450  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1451  if ( strokeElem.isNull() )
1452  return false;
1453 
1454  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1455  if ( graphicStrokeElem.isNull() )
1456  return false;
1457 
1458  return hasWellKnownMark( graphicStrokeElem );
1459 }
1460 
1462 {
1463  QDomElement fillElem = element.firstChildElement( "Fill" );
1464  if ( fillElem.isNull() )
1465  return false;
1466 
1467  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1468  if ( graphicFillElem.isNull() )
1469  return false;
1470 
1471  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1472  if ( graphicElem.isNull() )
1473  return false;
1474 
1475  // line pattern fill uses horline wellknown marker with an angle
1476 
1477  QString name;
1478  QColor fillColor, borderColor;
1479  double size, borderWidth;
1480  Qt::PenStyle borderStyle;
1481  if ( !wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderStyle, borderWidth, size ) )
1482  return false;
1483 
1484  if ( name != "horline" )
1485  return false;
1486 
1487  QString angleFunc;
1488  if ( !rotationFromSldElement( graphicElem, angleFunc ) )
1489  return false;
1490 
1491  bool ok;
1492  double angle = angleFunc.toDouble( &ok );
1493  if ( !ok || qgsDoubleNear( angle, 0.0 ) )
1494  return false;
1495 
1496  return true;
1497 }
1498 
1500 {
1501  Q_UNUSED( element );
1502  return false;
1503 }
1504 
1506 {
1507  QDomElement fillElem = element.firstChildElement( "Fill" );
1508  if ( fillElem.isNull() )
1509  return false;
1510 
1511  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1512  if ( graphicFillElem.isNull() )
1513  return false;
1514 
1515  return hasExternalGraphic( graphicFillElem );
1516 }
1517 
1518 
1520 {
1521  QgsDebugMsg( "Entered." );
1522 
1523  /* SE 1.1 says about PolygonSymbolizer:
1524  if a point geometry is referenced instead of a polygon,
1525  then a small, square, ortho-normal polygon should be
1526  constructed for rendering.
1527  */
1528 
1529  QgsSymbolLayerV2List layers;
1530 
1531  // retrieve both Fill and Stroke elements
1532  QDomElement fillElem = element.firstChildElement( "Fill" );
1533  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1534 
1535  // first symbol layer
1536  {
1537  bool validFill = false, validBorder = false;
1538 
1539  // check for simple fill
1540  // Fill element can contain some SvgParameter elements
1541  QColor fillColor;
1542  Qt::BrushStyle fillStyle;
1543 
1544  if ( fillFromSld( fillElem, fillStyle, fillColor ) )
1545  validFill = true;
1546 
1547  // check for simple outline
1548  // Stroke element can contain some SvgParameter elements
1549  QColor borderColor;
1550  Qt::PenStyle borderStyle;
1551  double borderWidth = 1.0, dashOffset = 0.0;
1552  QVector<qreal> customDashPattern;
1553 
1554  if ( lineFromSld( strokeElem, borderStyle, borderColor, borderWidth,
1555  nullptr, nullptr, &customDashPattern, &dashOffset ) )
1556  validBorder = true;
1557 
1558  if ( validFill || validBorder )
1559  {
1560  QgsStringMap map;
1561  map["name"] = "square";
1562  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1563  map["color_border"] = encodeColor( validBorder ? borderColor : Qt::transparent );
1564  map["size"] = QString::number( 6 );
1565  map["angle"] = QString::number( 0 );
1566  map["offset"] = encodePoint( QPointF( 0, 0 ) );
1567  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SimpleMarker", map ) );
1568  }
1569  }
1570 
1571  // second symbol layer
1572  {
1573  bool validFill = false, validBorder = false;
1574 
1575  // check for graphic fill
1576  QString name, format;
1577  int markIndex = -1;
1578  QColor fillColor, borderColor;
1579  double borderWidth = 1.0, size = 0.0, angle = 0.0;
1580  QPointF anchor, offset;
1581 
1582  // Fill element can contain a GraphicFill element
1583  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1584  if ( !graphicFillElem.isNull() )
1585  {
1586  // GraphicFill element must contain a Graphic element
1587  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1588  if ( !graphicElem.isNull() )
1589  {
1590  // Graphic element can contains some ExternalGraphic and Mark element
1591  // search for the first supported one and use it
1592  bool found = false;
1593 
1594  QDomElement graphicChildElem = graphicElem.firstChildElement();
1595  while ( !graphicChildElem.isNull() )
1596  {
1597  if ( graphicChildElem.localName() == "Mark" )
1598  {
1599  // check for a well known name
1600  QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( "WellKnownName" );
1601  if ( !wellKnownNameElem.isNull() )
1602  {
1603  name = wellKnownNameElem.firstChild().nodeValue();
1604  found = true;
1605  break;
1606  }
1607  }
1608 
1609  if ( graphicChildElem.localName() == "ExternalGraphic" || graphicChildElem.localName() == "Mark" )
1610  {
1611  // check for external graphic format
1612  QDomElement formatElem = graphicChildElem.firstChildElement( "Format" );
1613  if ( formatElem.isNull() )
1614  continue;
1615 
1616  format = formatElem.firstChild().nodeValue();
1617 
1618  // TODO: remove this check when more formats will be supported
1619  // only SVG external graphics are supported in this moment
1620  if ( graphicChildElem.localName() == "ExternalGraphic" && format != "image/svg+xml" )
1621  continue;
1622 
1623  // TODO: remove this check when more formats will be supported
1624  // only ttf marks are supported in this moment
1625  if ( graphicChildElem.localName() == "Mark" && format != "ttf" )
1626  continue;
1627 
1628  // check for a valid content
1629  QDomElement onlineResourceElem = graphicChildElem.firstChildElement( "OnlineResource" );
1630  QDomElement inlineContentElem = graphicChildElem.firstChildElement( "InlineContent" );
1631 
1632  if ( !onlineResourceElem.isNull() )
1633  {
1634  name = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
1635 
1636  if ( graphicChildElem.localName() == "Mark" && format == "ttf" )
1637  {
1638  // mark with ttf format may have a name like ttf://fontFamily
1639  if ( name.startsWith( "ttf://" ) )
1640  name = name.mid( 6 );
1641 
1642  // mark with ttf format has a markIndex element
1643  QDomElement markIndexElem = graphicChildElem.firstChildElement( "MarkIndex" );
1644  if ( markIndexElem.isNull() )
1645  continue;
1646 
1647  bool ok;
1648  int v = markIndexElem.firstChild().nodeValue().toInt( &ok );
1649  if ( !ok || v < 0 )
1650  continue;
1651 
1652  markIndex = v;
1653  }
1654 
1655  found = true;
1656  break;
1657  }
1658 #if 0
1659  else if ( !inlineContentElem.isNull() )
1660  continue; // TODO: not implemented yet
1661 #endif
1662  else
1663  continue;
1664  }
1665 
1666  // if Mark element is present but it doesn't contains neither
1667  // WellKnownName nor OnlineResource nor InlineContent,
1668  // use the default mark (square)
1669  if ( graphicChildElem.localName() == "Mark" )
1670  {
1671  name = "square";
1672  found = true;
1673  break;
1674  }
1675  }
1676 
1677  // if found a valid Mark, check for its Fill and Stroke element
1678  if ( found && graphicChildElem.localName() == "Mark" )
1679  {
1680  // XXX: recursive definition!?! couldn't be dangerous???
1681  // to avoid recursion we handle only simple fill and simple stroke
1682 
1683  // check for simple fill
1684  // Fill element can contain some SvgParameter elements
1685  Qt::BrushStyle markFillStyle;
1686 
1687  QDomElement markFillElem = graphicChildElem.firstChildElement( "Fill" );
1688  if ( fillFromSld( markFillElem, markFillStyle, fillColor ) )
1689  validFill = true;
1690 
1691  // check for simple outline
1692  // Stroke element can contain some SvgParameter elements
1693  Qt::PenStyle borderStyle;
1694  double borderWidth = 1.0, dashOffset = 0.0;
1695  QVector<qreal> customDashPattern;
1696 
1697  QDomElement markStrokeElem = graphicChildElem.firstChildElement( "Stroke" );
1698  if ( lineFromSld( markStrokeElem, borderStyle, borderColor, borderWidth,
1699  nullptr, nullptr, &customDashPattern, &dashOffset ) )
1700  validBorder = true;
1701  }
1702 
1703  if ( found )
1704  {
1705  // check for Opacity, Size, Rotation, AnchorPoint, Displacement
1706  QDomElement opacityElem = graphicElem.firstChildElement( "Opacity" );
1707  if ( !opacityElem.isNull() )
1708  fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) );
1709 
1710  QDomElement sizeElem = graphicElem.firstChildElement( "Size" );
1711  if ( !sizeElem.isNull() )
1712  {
1713  bool ok;
1714  double v = sizeElem.firstChild().nodeValue().toDouble( &ok );
1715  if ( ok && v > 0 )
1716  size = v;
1717  }
1718 
1719  QString angleFunc;
1720  if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() )
1721  {
1722  bool ok;
1723  double v = angleFunc.toDouble( &ok );
1724  if ( ok )
1725  angle = v;
1726  }
1727 
1728  displacementFromSldElement( graphicElem, offset );
1729  }
1730  }
1731  }
1732 
1733  if ( validFill || validBorder )
1734  {
1735  if ( format == "image/svg+xml" )
1736  {
1737  QgsStringMap map;
1738  map["name"] = name;
1739  map["fill"] = fillColor.name();
1740  map["outline"] = borderColor.name();
1741  map["outline-width"] = QString::number( borderWidth );
1742  if ( !qgsDoubleNear( size, 0.0 ) )
1743  map["size"] = QString::number( size );
1744  if ( !qgsDoubleNear( angle, 0.0 ) )
1745  map["angle"] = QString::number( angle );
1746  if ( !offset.isNull() )
1747  map["offset"] = encodePoint( offset );
1748  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "SvgMarker", map ) );
1749  }
1750  else if ( format == "ttf" )
1751  {
1752  QgsStringMap map;
1753  map["font"] = name;
1754  map["chr"] = markIndex;
1755  map["color"] = encodeColor( validFill ? fillColor : Qt::transparent );
1756  if ( size > 0 )
1757  map["size"] = QString::number( size );
1758  if ( !qgsDoubleNear( angle, 0.0 ) )
1759  map["angle"] = QString::number( angle );
1760  if ( !offset.isNull() )
1761  map["offset"] = encodePoint( offset );
1762  layers.append( QgsSymbolLayerV2Registry::instance()->createSymbolLayer( "FontMarker", map ) );
1763  }
1764  }
1765  }
1766 
1767  if ( layers.isEmpty() )
1768  return false;
1769 
1770  layerList << layers;
1771  layers.clear();
1772  return true;
1773 }
1774 
1775 void QgsSymbolLayerV2Utils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor& color )
1776 {
1777  QString patternName;
1778  switch ( brushStyle )
1779  {
1780  case Qt::NoBrush:
1781  return;
1782 
1783  case Qt::SolidPattern:
1784  if ( color.isValid() )
1785  {
1786  element.appendChild( createSvgParameterElement( doc, "fill", color.name() ) );
1787  if ( color.alpha() < 255 )
1788  element.appendChild( createSvgParameterElement( doc, "fill-opacity", encodeSldAlpha( color.alpha() ) ) );
1789  }
1790  return;
1791 
1792  case Qt::CrossPattern:
1793  case Qt::DiagCrossPattern:
1794  case Qt::HorPattern:
1795  case Qt::VerPattern:
1796  case Qt::BDiagPattern:
1797  case Qt::FDiagPattern:
1798  case Qt::Dense1Pattern:
1799  case Qt::Dense2Pattern:
1800  case Qt::Dense3Pattern:
1801  case Qt::Dense4Pattern:
1802  case Qt::Dense5Pattern:
1803  case Qt::Dense6Pattern:
1804  case Qt::Dense7Pattern:
1805  patternName = encodeSldBrushStyle( brushStyle );
1806  break;
1807 
1808  default:
1809  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) );
1810  return;
1811  }
1812 
1813  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1814  element.appendChild( graphicFillElem );
1815 
1816  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1817  graphicFillElem.appendChild( graphicElem );
1818 
1819  QColor fillColor = patternName.startsWith( "brush://" ) ? color : QColor();
1820  QColor borderColor = !patternName.startsWith( "brush://" ) ? color : QColor();
1821 
1822  /* Use WellKnownName tag to handle QT brush styles. */
1823  wellKnownMarkerToSld( doc, graphicElem, patternName, fillColor, borderColor, Qt::SolidLine, -1, -1 );
1824 }
1825 
1826 bool QgsSymbolLayerV2Utils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color )
1827 {
1828  QgsDebugMsg( "Entered." );
1829 
1830  brushStyle = Qt::SolidPattern;
1831  color = QColor( "#808080" );
1832 
1833  if ( element.isNull() )
1834  {
1835  brushStyle = Qt::NoBrush;
1836  color = QColor();
1837  return true;
1838  }
1839 
1840  QDomElement graphicFillElem = element.firstChildElement( "GraphicFill" );
1841  // if no GraphicFill element is found, it's a solid fill
1842  if ( graphicFillElem.isNull() )
1843  {
1844  QgsStringMap svgParams = getSvgParameterList( element );
1845  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1846  {
1847  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
1848 
1849  if ( it.key() == "fill" )
1850  color = QColor( it.value() );
1851  else if ( it.key() == "fill-opacity" )
1852  color.setAlpha( decodeSldAlpha( it.value() ) );
1853  }
1854  }
1855  else // wellKnown marker
1856  {
1857  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1858  if ( graphicElem.isNull() )
1859  return false; // Graphic is required within GraphicFill
1860 
1861  QString patternName = "square";
1862  QColor fillColor, borderColor;
1863  double borderWidth, size;
1864  Qt::PenStyle borderStyle;
1865  if ( !wellKnownMarkerFromSld( graphicElem, patternName, fillColor, borderColor, borderStyle, borderWidth, size ) )
1866  return false;
1867 
1868  brushStyle = decodeSldBrushStyle( patternName );
1869  if ( brushStyle == Qt::NoBrush )
1870  return false; // unable to decode brush style
1871 
1872  QColor c = patternName.startsWith( "brush://" ) ? fillColor : borderColor;
1873  if ( c.isValid() )
1874  color = c;
1875  }
1876 
1877  return true;
1878 }
1879 
1881  Qt::PenStyle penStyle, const QColor& color, double width,
1882  const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
1883  const QVector<qreal> *customDashPattern, double dashOffset )
1884 {
1885  QVector<qreal> dashPattern;
1886  const QVector<qreal> *pattern = &dashPattern;
1887 
1888  if ( penStyle == Qt::CustomDashLine && !customDashPattern )
1889  {
1890  element.appendChild( doc.createComment( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) );
1891  penStyle = Qt::DashLine;
1892  }
1893 
1894  switch ( penStyle )
1895  {
1896  case Qt::NoPen:
1897  return;
1898 
1899  case Qt::SolidLine:
1900  break;
1901 
1902  case Qt::DashLine:
1903  dashPattern.push_back( 4.0 );
1904  dashPattern.push_back( 2.0 );
1905  break;
1906  case Qt::DotLine:
1907  dashPattern.push_back( 1.0 );
1908  dashPattern.push_back( 2.0 );
1909  break;
1910  case Qt::DashDotLine:
1911  dashPattern.push_back( 4.0 );
1912  dashPattern.push_back( 2.0 );
1913  dashPattern.push_back( 1.0 );
1914  dashPattern.push_back( 2.0 );
1915  break;
1916  case Qt::DashDotDotLine:
1917  dashPattern.push_back( 4.0 );
1918  dashPattern.push_back( 2.0 );
1919  dashPattern.push_back( 1.0 );
1920  dashPattern.push_back( 2.0 );
1921  dashPattern.push_back( 1.0 );
1922  dashPattern.push_back( 2.0 );
1923  break;
1924 
1925  case Qt::CustomDashLine:
1926  Q_ASSERT( customDashPattern );
1927  pattern = customDashPattern;
1928  break;
1929 
1930  default:
1931  element.appendChild( doc.createComment( QString( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
1932  return;
1933  }
1934 
1935  if ( color.isValid() )
1936  {
1937  element.appendChild( createSvgParameterElement( doc, "stroke", color.name() ) );
1938  if ( color.alpha() < 255 )
1939  element.appendChild( createSvgParameterElement( doc, "stroke-opacity", encodeSldAlpha( color.alpha() ) ) );
1940  }
1941  if ( width > 0 )
1942  element.appendChild( createSvgParameterElement( doc, "stroke-width", QString::number( width ) ) );
1943  if ( penJoinStyle )
1944  element.appendChild( createSvgParameterElement( doc, "stroke-linejoin", encodeSldLineJoinStyle( *penJoinStyle ) ) );
1945  if ( penCapStyle )
1946  element.appendChild( createSvgParameterElement( doc, "stroke-linecap", encodeSldLineCapStyle( *penCapStyle ) ) );
1947 
1948  if ( !pattern->isEmpty() )
1949  {
1950  element.appendChild( createSvgParameterElement( doc, "stroke-dasharray", encodeSldRealVector( *pattern ) ) );
1951  if ( !qgsDoubleNear( dashOffset, 0.0 ) )
1952  element.appendChild( createSvgParameterElement( doc, "stroke-dashoffset", QString::number( dashOffset ) ) );
1953  }
1954 }
1955 
1956 
1958  Qt::PenStyle &penStyle, QColor &color, double &width,
1959  Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle,
1960  QVector<qreal> *customDashPattern, double *dashOffset )
1961 {
1962  QgsDebugMsg( "Entered." );
1963 
1964  penStyle = Qt::SolidLine;
1965  color = QColor( "#000000" );
1966  width = 1;
1967  if ( penJoinStyle )
1968  *penJoinStyle = Qt::BevelJoin;
1969  if ( penCapStyle )
1970  *penCapStyle = Qt::SquareCap;
1971  if ( customDashPattern )
1972  customDashPattern->clear();
1973  if ( dashOffset )
1974  *dashOffset = 0;
1975 
1976  if ( element.isNull() )
1977  {
1978  penStyle = Qt::NoPen;
1979  color = QColor();
1980  return true;
1981  }
1982 
1983  QgsStringMap svgParams = getSvgParameterList( element );
1984  for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
1985  {
1986  QgsDebugMsg( QString( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
1987 
1988  if ( it.key() == "stroke" )
1989  {
1990  color = QColor( it.value() );
1991  }
1992  else if ( it.key() == "stroke-opacity" )
1993  {
1994  color.setAlpha( decodeSldAlpha( it.value() ) );
1995  }
1996  else if ( it.key() == "stroke-width" )
1997  {
1998  bool ok;
1999  double w = it.value().toDouble( &ok );
2000  if ( ok )
2001  width = w;
2002  }
2003  else if ( it.key() == "stroke-linejoin" && penJoinStyle )
2004  {
2005  *penJoinStyle = decodeSldLineJoinStyle( it.value() );
2006  }
2007  else if ( it.key() == "stroke-linecap" && penCapStyle )
2008  {
2009  *penCapStyle = decodeSldLineCapStyle( it.value() );
2010  }
2011  else if ( it.key() == "stroke-dasharray" )
2012  {
2013  QVector<qreal> dashPattern = decodeSldRealVector( it.value() );
2014  if ( !dashPattern.isEmpty() )
2015  {
2016  // convert the dasharray to one of the QT pen style,
2017  // if no match is found then set pen style to CustomDashLine
2018  bool dashPatternFound = false;
2019 
2020  if ( dashPattern.count() == 2 )
2021  {
2022  if ( dashPattern.at( 0 ) == 4.0 &&
2023  dashPattern.at( 1 ) == 2.0 )
2024  {
2025  penStyle = Qt::DashLine;
2026  dashPatternFound = true;
2027  }
2028  else if ( dashPattern.at( 0 ) == 1.0 &&
2029  dashPattern.at( 1 ) == 2.0 )
2030  {
2031  penStyle = Qt::DotLine;
2032  dashPatternFound = true;
2033  }
2034  }
2035  else if ( dashPattern.count() == 4 )
2036  {
2037  if ( dashPattern.at( 0 ) == 4.0 &&
2038  dashPattern.at( 1 ) == 2.0 &&
2039  dashPattern.at( 2 ) == 1.0 &&
2040  dashPattern.at( 3 ) == 2.0 )
2041  {
2042  penStyle = Qt::DashDotLine;
2043  dashPatternFound = true;
2044  }
2045  }
2046  else if ( dashPattern.count() == 6 )
2047  {
2048  if ( dashPattern.at( 0 ) == 4.0 &&
2049  dashPattern.at( 1 ) == 2.0 &&
2050  dashPattern.at( 2 ) == 1.0 &&
2051  dashPattern.at( 3 ) == 2.0 &&
2052  dashPattern.at( 4 ) == 1.0 &&
2053  dashPattern.at( 5 ) == 2.0 )
2054  {
2055  penStyle = Qt::DashDotDotLine;
2056  dashPatternFound = true;
2057  }
2058  }
2059 
2060  // default case: set pen style to CustomDashLine
2061  if ( !dashPatternFound )
2062  {
2063  if ( customDashPattern )
2064  {
2065  penStyle = Qt::CustomDashLine;
2066  *customDashPattern = dashPattern;
2067  }
2068  else
2069  {
2070  QgsDebugMsg( "custom dash pattern required but not provided. Using default dash pattern." );
2071  penStyle = Qt::DashLine;
2072  }
2073  }
2074  }
2075  }
2076  else if ( it.key() == "stroke-dashoffset" && dashOffset )
2077  {
2078  bool ok;
2079  double d = it.value().toDouble( &ok );
2080  if ( ok )
2081  *dashOffset = d;
2082  }
2083  }
2084 
2085  return true;
2086 }
2087 
2089  const QString& path, const QString& mime,
2090  const QColor& color, double size )
2091 {
2092  QDomElement externalGraphicElem = doc.createElement( "se:ExternalGraphic" );
2093  element.appendChild( externalGraphicElem );
2094 
2095  createOnlineResourceElement( doc, externalGraphicElem, path, mime );
2096 
2097  //TODO: missing a way to handle svg color. Should use <se:ColorReplacement>
2098  Q_UNUSED( color );
2099 
2100  if ( size >= 0 )
2101  {
2102  QDomElement sizeElem = doc.createElement( "se:Size" );
2103  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2104  element.appendChild( sizeElem );
2105  }
2106 }
2107 
2109  QString &path, QString &mime,
2110  QColor &color, double &size )
2111 {
2112  QgsDebugMsg( "Entered." );
2113  Q_UNUSED( color );
2114 
2115  QDomElement externalGraphicElem = element.firstChildElement( "ExternalGraphic" );
2116  if ( externalGraphicElem.isNull() )
2117  return false;
2118 
2119  onlineResourceFromSldElement( externalGraphicElem, path, mime );
2120 
2121  QDomElement sizeElem = element.firstChildElement( "Size" );
2122  if ( !sizeElem.isNull() )
2123  {
2124  bool ok;
2125  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2126  if ( ok )
2127  size = s;
2128  }
2129 
2130  return true;
2131 }
2132 
2134  const QString& path, const QString& format, int *markIndex,
2135  const QColor& color, double size )
2136 {
2137  QDomElement markElem = doc.createElement( "se:Mark" );
2138  element.appendChild( markElem );
2139 
2140  createOnlineResourceElement( doc, markElem, path, format );
2141 
2142  if ( markIndex )
2143  {
2144  QDomElement markIndexElem = doc.createElement( "se:MarkIndex" );
2145  markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) );
2146  markElem.appendChild( markIndexElem );
2147  }
2148 
2149  // <Fill>
2150  QDomElement fillElem = doc.createElement( "se:Fill" );
2151  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2152  markElem.appendChild( fillElem );
2153 
2154  // <Size>
2155  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
2156  {
2157  QDomElement sizeElem = doc.createElement( "se:Size" );
2158  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2159  element.appendChild( sizeElem );
2160  }
2161 }
2162 
2164  QString &path, QString &format, int &markIndex,
2165  QColor &color, double &size )
2166 {
2167  QgsDebugMsg( "Entered." );
2168 
2169  color = QColor();
2170  markIndex = -1;
2171  size = -1;
2172 
2173  QDomElement markElem = element.firstChildElement( "Mark" );
2174  if ( markElem.isNull() )
2175  return false;
2176 
2177  onlineResourceFromSldElement( markElem, path, format );
2178 
2179  QDomElement markIndexElem = markElem.firstChildElement( "MarkIndex" );
2180  if ( !markIndexElem.isNull() )
2181  {
2182  bool ok;
2183  int i = markIndexElem.firstChild().nodeValue().toInt( &ok );
2184  if ( ok )
2185  markIndex = i;
2186  }
2187 
2188  // <Fill>
2189  QDomElement fillElem = markElem.firstChildElement( "Fill" );
2190  Qt::BrushStyle b = Qt::SolidPattern;
2191  fillFromSld( fillElem, b, color );
2192  // ignore brush style, solid expected
2193 
2194  // <Size>
2195  QDomElement sizeElem = element.firstChildElement( "Size" );
2196  if ( !sizeElem.isNull() )
2197  {
2198  bool ok;
2199  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2200  if ( ok )
2201  size = s;
2202  }
2203 
2204  return true;
2205 }
2206 
2208  const QString& name, const QColor& color, const QColor& borderColor,
2209  double borderWidth, double size )
2210 {
2211  wellKnownMarkerToSld( doc, element, name, color, borderColor, Qt::SolidLine, borderWidth, size );
2212 }
2213 
2215  const QString& name, const QColor& color, const QColor& borderColor, Qt::PenStyle borderStyle,
2216  double borderWidth, double size )
2217 {
2218  QDomElement markElem = doc.createElement( "se:Mark" );
2219  element.appendChild( markElem );
2220 
2221  QDomElement wellKnownNameElem = doc.createElement( "se:WellKnownName" );
2222  wellKnownNameElem.appendChild( doc.createTextNode( name ) );
2223  markElem.appendChild( wellKnownNameElem );
2224 
2225  // <Fill>
2226  if ( color.isValid() )
2227  {
2228  QDomElement fillElem = doc.createElement( "se:Fill" );
2229  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2230  markElem.appendChild( fillElem );
2231  }
2232 
2233  // <Stroke>
2234  if ( borderColor.isValid() )
2235  {
2236  QDomElement strokeElem = doc.createElement( "se:Stroke" );
2237  lineToSld( doc, strokeElem, borderStyle, borderColor, borderWidth );
2238  markElem.appendChild( strokeElem );
2239  }
2240 
2241  // <Size>
2242  if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
2243  {
2244  QDomElement sizeElem = doc.createElement( "se:Size" );
2245  sizeElem.appendChild( doc.createTextNode( QString::number( size ) ) );
2246  element.appendChild( sizeElem );
2247  }
2248 }
2249 
2251  QString &name, QColor &color, QColor &borderColor,
2252  double &borderWidth, double &size )
2253 {
2254  Qt::PenStyle borderStyle;
2255  return wellKnownMarkerFromSld( element, name, color, borderColor, borderStyle, borderWidth, size );
2256 }
2257 
2259  QString &name, QColor &color, QColor &borderColor, Qt::PenStyle &borderStyle,
2260  double &borderWidth, double &size )
2261 {
2262  QgsDebugMsg( "Entered." );
2263 
2264  name = "square";
2265  color = QColor();
2266  borderColor = QColor( "#000000" );
2267  borderWidth = 1;
2268  size = 6;
2269 
2270  QDomElement markElem = element.firstChildElement( "Mark" );
2271  if ( markElem.isNull() )
2272  return false;
2273 
2274  QDomElement wellKnownNameElem = markElem.firstChildElement( "WellKnownName" );
2275  if ( !wellKnownNameElem.isNull() )
2276  {
2277  name = wellKnownNameElem.firstChild().nodeValue();
2278  QgsDebugMsg( "found Mark with well known name: " + name );
2279  }
2280 
2281  // <Fill>
2282  QDomElement fillElem = markElem.firstChildElement( "Fill" );
2283  Qt::BrushStyle b = Qt::SolidPattern;
2284  fillFromSld( fillElem, b, color );
2285  // ignore brush style, solid expected
2286 
2287  // <Stroke>
2288  QDomElement strokeElem = markElem.firstChildElement( "Stroke" );
2289  lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
2290  // ignore border style, solid expected
2291 
2292  // <Size>
2293  QDomElement sizeElem = element.firstChildElement( "Size" );
2294  if ( !sizeElem.isNull() )
2295  {
2296  bool ok;
2297  double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
2298  if ( ok )
2299  size = s;
2300  }
2301 
2302  return true;
2303 }
2304 
2306 {
2307  if ( !rotationFunc.isEmpty() )
2308  {
2309  QDomElement rotationElem = doc.createElement( "se:Rotation" );
2310  createExpressionElement( doc, rotationElem, rotationFunc );
2311  element.appendChild( rotationElem );
2312  }
2313 }
2314 
2316 {
2317  QDomElement rotationElem = element.firstChildElement( "Rotation" );
2318  if ( !rotationElem.isNull() )
2319  {
2320  return functionFromSldElement( rotationElem, rotationFunc );
2321  }
2322  return true;
2323 }
2324 
2325 
2327 {
2328  if ( !alphaFunc.isEmpty() )
2329  {
2330  QDomElement opacityElem = doc.createElement( "se:Opacity" );
2331  createExpressionElement( doc, opacityElem, alphaFunc );
2332  element.appendChild( opacityElem );
2333  }
2334 }
2335 
2337 {
2338  QDomElement opacityElem = element.firstChildElement( "Opacity" );
2339  if ( !opacityElem.isNull() )
2340  {
2341  return functionFromSldElement( opacityElem, alphaFunc );
2342  }
2343  return true;
2344 }
2345 
2347 {
2348  if ( offset.isNull() )
2349  return;
2350 
2351  QDomElement displacementElem = doc.createElement( "se:Displacement" );
2352  element.appendChild( displacementElem );
2353 
2354  QDomElement dispXElem = doc.createElement( "se:DisplacementX" );
2355  dispXElem.appendChild( doc.createTextNode( QString::number( offset.x() ) ) );
2356 
2357  QDomElement dispYElem = doc.createElement( "se:DisplacementY" );
2358  dispYElem.appendChild( doc.createTextNode( QString::number( offset.y() ) ) );
2359 
2360  displacementElem.appendChild( dispXElem );
2361  displacementElem.appendChild( dispYElem );
2362 }
2363 
2365 {
2366  offset = QPointF( 0, 0 );
2367 
2368  QDomElement displacementElem = element.firstChildElement( "Displacement" );
2369  if ( displacementElem.isNull() )
2370  return true;
2371 
2372  QDomElement dispXElem = displacementElem.firstChildElement( "DisplacementX" );
2373  if ( !dispXElem.isNull() )
2374  {
2375  bool ok;
2376  double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok );
2377  if ( ok )
2378  offset.setX( offsetX );
2379  }
2380 
2381  QDomElement dispYElem = displacementElem.firstChildElement( "DisplacementY" );
2382  if ( !dispYElem.isNull() )
2383  {
2384  bool ok;
2385  double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok );
2386  if ( ok )
2387  offset.setY( offsetY );
2388  }
2389 
2390  return true;
2391 }
2392 
2394  const QString& label, const QFont& font,
2395  const QColor& color, double size )
2396 {
2397  QDomElement labelElem = doc.createElement( "se:Label" );
2398  labelElem.appendChild( doc.createTextNode( label ) );
2399  element.appendChild( labelElem );
2400 
2401  QDomElement fontElem = doc.createElement( "se:Font" );
2402  element.appendChild( fontElem );
2403 
2404  fontElem.appendChild( createSvgParameterElement( doc, "font-family", font.family() ) );
2405 #if 0
2406  fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) );
2407  fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) );
2408 #endif
2409  fontElem.appendChild( createSvgParameterElement( doc, "font-size", QString::number( size ) ) );
2410 
2411  // <Fill>
2412  if ( color.isValid() )
2413  {
2414  QDomElement fillElem = doc.createElement( "Fill" );
2415  fillToSld( doc, fillElem, Qt::SolidPattern, color );
2416  element.appendChild( fillElem );
2417  }
2418 }
2419 
2420 QString QgsSymbolLayerV2Utils::ogrFeatureStylePen( double width, double mmScaleFactor, double mapUnitScaleFactor, const QColor& c,
2421  Qt::PenJoinStyle joinStyle,
2422  Qt::PenCapStyle capStyle,
2423  double offset,
2424  const QVector<qreal>* dashPattern )
2425 {
2426  QString penStyle;
2427  penStyle.append( "PEN(" );
2428  penStyle.append( "c:" );
2429  penStyle.append( c.name() );
2430  penStyle.append( ",w:" );
2431  //dxf driver writes ground units as mm? Should probably be changed in ogr
2432  penStyle.append( QString::number( width * mmScaleFactor ) );
2433  penStyle.append( "mm" );
2434 
2435  //dash dot vector
2436  if ( dashPattern && !dashPattern->isEmpty() )
2437  {
2438  penStyle.append( ",p:\"" );
2439  QVector<qreal>::const_iterator pIt = dashPattern->constBegin();
2440  for ( ; pIt != dashPattern->constEnd(); ++pIt )
2441  {
2442  if ( pIt != dashPattern->constBegin() )
2443  {
2444  penStyle.append( ' ' );
2445  }
2446  penStyle.append( QString::number( *pIt * mapUnitScaleFactor ) );
2447  penStyle.append( 'g' );
2448  }
2449  penStyle.append( '\"' );
2450  }
2451 
2452  //cap
2453  penStyle.append( ",cap:" );
2454  switch ( capStyle )
2455  {
2456  case Qt::SquareCap:
2457  penStyle.append( 'p' );
2458  break;
2459  case Qt::RoundCap:
2460  penStyle.append( 'r' );
2461  break;
2462  case Qt::FlatCap:
2463  default:
2464  penStyle.append( 'b' );
2465  }
2466 
2467  //join
2468  penStyle.append( ",j:" );
2469  switch ( joinStyle )
2470  {
2471  case Qt::BevelJoin:
2472  penStyle.append( 'b' );
2473  break;
2474  case Qt::RoundJoin:
2475  penStyle.append( 'r' );
2476  break;
2477  case Qt::MiterJoin:
2478  default:
2479  penStyle.append( 'm' );
2480  }
2481 
2482  //offset
2483  if ( !qgsDoubleNear( offset, 0.0 ) )
2484  {
2485  penStyle.append( ",dp:" );
2486  penStyle.append( QString::number( offset * mapUnitScaleFactor ) );
2487  penStyle.append( 'g' );
2488  }
2489 
2490  penStyle.append( ')' );
2491  return penStyle;
2492 }
2493 
2495 {
2496  QString brushStyle;
2497  brushStyle.append( "BRUSH(" );
2498  brushStyle.append( "fc:" );
2499  brushStyle.append( fillColor.name() );
2500  brushStyle.append( ')' );
2501  return brushStyle;
2502 }
2503 
2505 {
2506  if ( geomFunc.isEmpty() )
2507  return;
2508 
2509  QDomElement geometryElem = doc.createElement( "Geometry" );
2510  element.appendChild( geometryElem );
2511 
2512  /* About using a function withing the Geometry tag.
2513  *
2514  * The SLD specification <= 1.1 is vague:
2515  * "In principle, a fixed geometry could be defined using GML or
2516  * operators could be defined for computing the geometry from
2517  * references or literals. However, using a feature property directly
2518  * is by far the most commonly useful method."
2519  *
2520  * Even if it seems that specs should take care all the possible cases,
2521  * looking at the XML schema fragment that encodes the Geometry element,
2522  * it has to be a PropertyName element:
2523  * <xsd:element name="Geometry">
2524  * <xsd:complexType>
2525  * <xsd:sequence>
2526  * <xsd:element ref="ogc:PropertyName"/>
2527  * </xsd:sequence>
2528  * </xsd:complexType>
2529  * </xsd:element>
2530  *
2531  * Anyway we will use a ogc:Function to handle geometry transformations
2532  * like offset, centroid, ...
2533  */
2534 
2535  createExpressionElement( doc, geometryElem, geomFunc );
2536 }
2537 
2539 {
2540  QDomElement geometryElem = element.firstChildElement( "Geometry" );
2541  if ( geometryElem.isNull() )
2542  return true;
2543 
2544  return functionFromSldElement( geometryElem, geomFunc );
2545 }
2546 
2548 {
2549  // let's use QgsExpression to generate the SLD for the function
2550  QgsExpression expr( function );
2551  if ( expr.hasParserError() )
2552  {
2553  element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
2554  return false;
2555  }
2556  QDomElement filterElem = QgsOgcUtils::expressionToOgcExpression( expr, doc );
2557  if ( !filterElem.isNull() )
2558  element.appendChild( filterElem );
2559  return true;
2560 }
2561 
2563 {
2564  // let's use QgsExpression to generate the SLD for the function
2565  QgsExpression expr( function );
2566  if ( expr.hasParserError() )
2567  {
2568  element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
2569  return false;
2570  }
2571  QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
2572  if ( !filterElem.isNull() )
2573  element.appendChild( filterElem );
2574  return true;
2575 }
2576 
2578 {
2579  QDomElement elem = element;
2580  if ( element.tagName() != "Filter" )
2581  {
2582  QDomNodeList filterNodes = element.elementsByTagName( "Filter" );
2583  if ( !filterNodes.isEmpty() )
2584  {
2585  elem = filterNodes.at( 0 ).toElement();
2586  }
2587  }
2588 
2589  if ( elem.isNull() )
2590  {
2591  return false;
2592  }
2593 
2594 
2596  if ( !expr )
2597  return false;
2598 
2599  bool valid = !expr->hasParserError();
2600  if ( !valid )
2601  {
2602  QgsDebugMsg( "parser error: " + expr->parserErrorString() );
2603  }
2604  else
2605  {
2606  function = expr->expression();
2607  }
2608 
2609  delete expr;
2610  return valid;
2611 }
2612 
2614  const QString& path, const QString& format )
2615 {
2616  // get resource url or relative path
2617  QString url = symbolPathToName( path );
2618  QDomElement onlineResourceElem = doc.createElement( "se:OnlineResource" );
2619  onlineResourceElem.setAttribute( "xlink:type", "simple" );
2620  onlineResourceElem.setAttribute( "xlink:href", url );
2621  element.appendChild( onlineResourceElem );
2622 
2623  QDomElement formatElem = doc.createElement( "se:Format" );
2624  formatElem.appendChild( doc.createTextNode( format ) );
2625  element.appendChild( formatElem );
2626 }
2627 
2629 {
2630  QgsDebugMsg( "Entered." );
2631 
2632  QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
2633  if ( onlineResourceElem.isNull() )
2634  return false;
2635 
2636  path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
2637 
2638  QDomElement formatElem = element.firstChildElement( "Format" );
2639  if ( formatElem.isNull() )
2640  return false; // OnlineResource requires a Format sibling element
2641 
2642  format = formatElem.firstChild().nodeValue();
2643  return true;
2644 }
2645 
2646 
2648 {
2649  QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
2650  nodeElem.setAttribute( "name", name );
2651  nodeElem.appendChild( doc.createTextNode( value ) );
2652  return nodeElem;
2653 }
2654 
2656 {
2657  QgsStringMap params;
2658 
2659  QDomElement paramElem = element.firstChildElement();
2660  while ( !paramElem.isNull() )
2661  {
2662  if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
2663  {
2664  QString name = paramElem.attribute( "name" );
2665  QString value = paramElem.firstChild().nodeValue();
2666 
2667  if ( !name.isEmpty() && !value.isEmpty() )
2668  params[ name ] = value;
2669  }
2670 
2671  paramElem = paramElem.nextSiblingElement();
2672  }
2673 
2674  return params;
2675 }
2676 
2678 {
2679  QDomElement nodeElem = doc.createElement( "VendorOption" );
2680  nodeElem.setAttribute( "name", name );
2681  nodeElem.appendChild( doc.createTextNode( value ) );
2682  return nodeElem;
2683 }
2684 
2686 {
2687  QgsStringMap params;
2688 
2689  QDomElement paramElem = element.firstChildElement( "VendorOption" );
2690  while ( !paramElem.isNull() )
2691  {
2692  QString name = paramElem.attribute( "name" );
2693  QString value = paramElem.firstChild().nodeValue();
2694 
2695  if ( !name.isEmpty() && !value.isEmpty() )
2696  params[ name ] = value;
2697 
2698  paramElem = paramElem.nextSiblingElement( "VendorOption" );
2699  }
2700 
2701  return params;
2702 }
2703 
2704 
2706 {
2707  QgsStringMap props;
2708  QDomElement e = element.firstChildElement();
2709  while ( !e.isNull() )
2710  {
2711  if ( e.tagName() != "prop" )
2712  {
2713  QgsDebugMsg( "unknown tag " + e.tagName() );
2714  }
2715  else
2716  {
2717  QString propKey = e.attribute( "k" );
2718  QString propValue = e.attribute( "v" );
2719  props[propKey] = propValue;
2720  }
2721  e = e.nextSiblingElement();
2722  }
2723  return props;
2724 }
2725 
2726 
2728 {
2729  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
2730  {
2731  QDomElement propEl = doc.createElement( "prop" );
2732  propEl.setAttribute( "k", it.key() );
2733  propEl.setAttribute( "v", it.value() );
2734  element.appendChild( propEl );
2735  }
2736 }
2737 
2739 {
2740  // go through symbols one-by-one and load them
2741 
2742  QgsSymbolV2Map symbols;
2743  QDomElement e = element.firstChildElement();
2744 
2745  while ( !e.isNull() )
2746  {
2747  if ( e.tagName() == "symbol" )
2748  {
2750  if ( symbol )
2751  symbols.insert( e.attribute( "name" ), symbol );
2752  }
2753  else
2754  {
2755  QgsDebugMsg( "unknown tag: " + e.tagName() );
2756  }
2757  e = e.nextSiblingElement();
2758  }
2759 
2760 
2761  // now walk through the list of symbols and find those prefixed with @
2762  // these symbols are sub-symbols of some other symbol layers
2763  // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo"
2764  QStringList subsymbols;
2765 
2766  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2767  {
2768  if ( it.key()[0] != '@' )
2769  continue;
2770 
2771  // add to array (for deletion)
2772  subsymbols.append( it.key() );
2773 
2774  QStringList parts = it.key().split( '@' );
2775  if ( parts.count() < 3 )
2776  {
2777  QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
2778  delete it.value(); // we must delete it
2779  continue; // some invalid syntax
2780  }
2781  QString symname = parts[1];
2782  int symlayer = parts[2].toInt();
2783 
2784  if ( !symbols.contains( symname ) )
2785  {
2786  QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
2787  delete it.value(); // we must delete it
2788  continue;
2789  }
2790 
2791  QgsSymbolV2* sym = symbols[symname];
2792  if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
2793  {
2794  QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
2795  delete it.value(); // we must delete it
2796  continue;
2797  }
2798 
2799  // set subsymbol takes ownership
2800  bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
2801  if ( !res )
2802  {
2803  QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
2804  }
2805 
2806 
2807  }
2808 
2809  // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
2810  for ( int i = 0; i < subsymbols.count(); i++ )
2811  symbols.take( subsymbols[i] );
2812 
2813  return symbols;
2814 }
2815 
2817 {
2818  QDomElement symbolsElem = doc.createElement( tagName );
2819 
2820  // save symbols
2821  for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its )
2822  {
2823  QDomElement symEl = saveSymbol( its.key(), its.value(), doc );
2824  symbolsElem.appendChild( symEl );
2825  }
2826 
2827  return symbolsElem;
2828 }
2829 
2831 {
2832  qDeleteAll( symbols );
2833  symbols.clear();
2834 }
2835 
2836 
2838 {
2839  QString rampType = element.attribute( "type" );
2840 
2841  // parse properties
2843 
2844  if ( rampType == "gradient" )
2845  return QgsVectorGradientColorRampV2::create( props );
2846  else if ( rampType == "random" )
2847  return QgsVectorRandomColorRampV2::create( props );
2848  else if ( rampType == "colorbrewer" )
2850  else if ( rampType == "cpt-city" )
2851  return QgsCptCityColorRampV2::create( props );
2852  else
2853  {
2854  QgsDebugMsg( "unknown colorramp type " + rampType );
2855  return nullptr;
2856  }
2857 }
2858 
2859 
2861 {
2862  QDomElement rampEl = doc.createElement( "colorramp" );
2863  rampEl.setAttribute( "type", ramp->type() );
2864  rampEl.setAttribute( "name", name );
2865 
2866  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2867  return rampEl;
2868 }
2869 
2871 {
2872  if ( !color.isValid() )
2873  {
2874  return QString();
2875  }
2876 
2877  //TODO - utilise a color names database (such as X11) to return nicer names
2878  //for now, just return hex codes
2879  return color.name();
2880 }
2881 
2883 {
2884  QList<QColor> colors;
2885 
2886  //try splitting string at commas, spaces or newlines
2887  QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
2888  QStringList::iterator it = components.begin();
2889  for ( ; it != components.end(); ++it )
2890  {
2891  QColor result = parseColor( *it, true );
2892  if ( result.isValid() )
2893  {
2894  colors << result;
2895  }
2896  }
2897  if ( colors.length() > 0 )
2898  {
2899  return colors;
2900  }
2901 
2902  //try splitting string at commas or newlines
2903  components = colorStr.split( QRegExp( "(,|\n)" ) );
2904  it = components.begin();
2905  for ( ; it != components.end(); ++it )
2906  {
2907  QColor result = parseColor( *it, true );
2908  if ( result.isValid() )
2909  {
2910  colors << result;
2911  }
2912  }
2913  if ( colors.length() > 0 )
2914  {
2915  return colors;
2916  }
2917 
2918  //try splitting string at whitespace or newlines
2919  components = colorStr.simplified().split( QString( ' ' ) );
2920  it = components.begin();
2921  for ( ; it != components.end(); ++it )
2922  {
2923  QColor result = parseColor( *it, true );
2924  if ( result.isValid() )
2925  {
2926  colors << result;
2927  }
2928  }
2929  if ( colors.length() > 0 )
2930  {
2931  return colors;
2932  }
2933 
2934  //try splitting string just at newlines
2935  components = colorStr.split( '\n' );
2936  it = components.begin();
2937  for ( ; it != components.end(); ++it )
2938  {
2939  QColor result = parseColor( *it, true );
2940  if ( result.isValid() )
2941  {
2942  colors << result;
2943  }
2944  }
2945 
2946  return colors;
2947 }
2948 
2950 {
2951  //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
2952  //value, and can be used when pasting colors outside of QGIS).
2953  QMimeData *mimeData = new QMimeData;
2954  mimeData->setColorData( QVariant( color ) );
2955  mimeData->setText( color.name() );
2956  return mimeData;
2957 }
2958 
2959 QColor QgsSymbolLayerV2Utils::colorFromMimeData( const QMimeData * mimeData, bool& hasAlpha )
2960 {
2961  //attempt to read color data directly from mime
2962  QColor mimeColor = mimeData->colorData().value<QColor>();
2963  if ( mimeColor.isValid() )
2964  {
2965  hasAlpha = true;
2966  return mimeColor;
2967  }
2968 
2969  //attempt to intrepret a color from mime text data
2970  hasAlpha = false;
2971  QColor textColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( mimeData->text(), hasAlpha );
2972  if ( textColor.isValid() )
2973  {
2974  return textColor;
2975  }
2976 
2977  //could not get color from mime data
2978  return QColor();
2979 }
2980 
2982 {
2983  QgsNamedColorList mimeColors;
2984 
2985  //prefer xml format
2986  if ( data->hasFormat( "text/xml" ) )
2987  {
2988  //get XML doc
2989  QByteArray encodedData = data->data( "text/xml" );
2990  QDomDocument xmlDoc;
2991  xmlDoc.setContent( encodedData );
2992 
2993  QDomElement dragDataElem = xmlDoc.documentElement();
2994  if ( dragDataElem.tagName() == "ColorSchemeModelDragData" )
2995  {
2996  QDomNodeList nodeList = dragDataElem.childNodes();
2997  int nChildNodes = nodeList.size();
2998  QDomElement currentElem;
2999 
3000  for ( int i = 0; i < nChildNodes; ++i )
3001  {
3002  currentElem = nodeList.at( i ).toElement();
3003  if ( currentElem.isNull() )
3004  {
3005  continue;
3006  }
3007 
3008  QPair< QColor, QString> namedColor;
3009  namedColor.first = QgsSymbolLayerV2Utils::decodeColor( currentElem.attribute( "color", "255,255,255,255" ) );
3010  namedColor.second = currentElem.attribute( "label", "" );
3011 
3012  mimeColors << namedColor;
3013  }
3014  }
3015  }
3016 
3017  if ( mimeColors.length() == 0 && data->hasFormat( "application/x-colorobject-list" ) )
3018  {
3019  //get XML doc
3020  QByteArray encodedData = data->data( "application/x-colorobject-list" );
3021  QDomDocument xmlDoc;
3022  xmlDoc.setContent( encodedData );
3023 
3024  QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QString( "colors" ) );
3025  if ( colorsNodes.length() > 0 )
3026  {
3027  QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
3028  QDomNodeList colorNodeList = colorsElem.childNodes();
3029  int nChildNodes = colorNodeList.size();
3030  QDomElement currentElem;
3031 
3032  for ( int i = 0; i < nChildNodes; ++i )
3033  {
3034  //li element
3035  currentElem = colorNodeList.at( i ).toElement();
3036  if ( currentElem.isNull() )
3037  {
3038  continue;
3039  }
3040 
3041  QDomNodeList colorNodes = currentElem.elementsByTagName( QString( "color" ) );
3042  QDomNodeList nameNodes = currentElem.elementsByTagName( QString( "name" ) );
3043 
3044  if ( colorNodes.length() > 0 )
3045  {
3046  QDomElement colorElem = colorNodes.at( 0 ).toElement();
3047 
3048  QStringList colorParts = colorElem.text().simplified().split( ' ' );
3049  if ( colorParts.length() < 3 )
3050  {
3051  continue;
3052  }
3053 
3054  int red = colorParts.at( 0 ).toDouble() * 255;
3055  int green = colorParts.at( 1 ).toDouble() * 255;
3056  int blue = colorParts.at( 2 ).toDouble() * 255;
3057  QPair< QColor, QString> namedColor;
3058  namedColor.first = QColor( red, green, blue );
3059  if ( nameNodes.length() > 0 )
3060  {
3061  QDomElement nameElem = nameNodes.at( 0 ).toElement();
3062  namedColor.second = nameElem.text();
3063  }
3064  mimeColors << namedColor;
3065  }
3066  }
3067  }
3068  }
3069 
3070  if ( mimeColors.length() == 0 && data->hasText() )
3071  {
3072  //attempt to read color data from mime text
3074  QList< QColor >::iterator it = parsedColors.begin();
3075  for ( ; it != parsedColors.end(); ++it )
3076  {
3077  mimeColors << qMakePair( *it, QString() );
3078  }
3079  }
3080 
3081  if ( mimeColors.length() == 0 && data->hasColor() )
3082  {
3083  //attempt to read color data directly from mime
3084  QColor mimeColor = data->colorData().value<QColor>();
3085  if ( mimeColor.isValid() )
3086  {
3087  mimeColors << qMakePair( mimeColor, QString() );
3088  }
3089  }
3090 
3091  return mimeColors;
3092 }
3093 
3095 {
3096  //native format
3097  QMimeData* mimeData = new QMimeData();
3098  QDomDocument xmlDoc;
3099  QDomElement xmlRootElement = xmlDoc.createElement( "ColorSchemeModelDragData" );
3100  xmlDoc.appendChild( xmlRootElement );
3101 
3102  QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
3103  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3104  {
3105  QDomElement namedColor = xmlDoc.createElement( "NamedColor" );
3106  namedColor.setAttribute( "color", QgsSymbolLayerV2Utils::encodeColor(( *colorIt ).first ) );
3107  namedColor.setAttribute( "label", ( *colorIt ).second );
3108  xmlRootElement.appendChild( namedColor );
3109  }
3110  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
3111 
3112  if ( !allFormats )
3113  {
3114  return mimeData;
3115  }
3116 
3117  //set mime text to list of hex values
3118  colorIt = colorList.constBegin();
3119  QStringList colorListString;
3120  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3121  {
3122  colorListString << ( *colorIt ).first.name();
3123  }
3124  mimeData->setText( colorListString.join( "\n" ) );
3125 
3126  //set mime color data to first color
3127  if ( colorList.length() > 0 )
3128  {
3129  mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
3130  }
3131 
3132  return mimeData;
3133 }
3134 
3135 bool QgsSymbolLayerV2Utils::saveColorsToGpl( QFile &file, const QString& paletteName, const QgsNamedColorList& colors )
3136 {
3137  if ( !file.open( QIODevice::ReadWrite ) )
3138  {
3139  return false;
3140  }
3141 
3142  QTextStream stream( &file );
3143  stream << "GIMP Palette" << endl;
3144  if ( paletteName.isEmpty() )
3145  {
3146  stream << "Name: QGIS Palette" << endl;
3147  }
3148  else
3149  {
3150  stream << "Name: " << paletteName << endl;
3151  }
3152  stream << "Columns: 4" << endl;
3153  stream << '#' << endl;
3154 
3155  for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
3156  {
3157  QColor color = ( *colorIt ).first;
3158  if ( !color.isValid() )
3159  {
3160  continue;
3161  }
3162  stream << QString( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
3163  stream << "\t" << (( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
3164  }
3165  file.close();
3166 
3167  return true;
3168 }
3169 
3171 {
3172  QgsNamedColorList importedColors;
3173 
3174  if ( !file.open( QIODevice::ReadOnly ) )
3175  {
3176  ok = false;
3177  return importedColors;
3178  }
3179 
3180  QTextStream in( &file );
3181 
3182  QString line = in.readLine();
3183  if ( !line.startsWith( "GIMP Palette" ) )
3184  {
3185  ok = false;
3186  return importedColors;
3187  }
3188 
3189  //find name line
3190  while ( !in.atEnd() && !line.startsWith( "Name:" ) && !line.startsWith( '#' ) )
3191  {
3192  line = in.readLine();
3193  }
3194  if ( line.startsWith( "Name:" ) )
3195  {
3196  QRegExp nameRx( "Name:\\s*(\\S.*)$" );
3197  if ( nameRx.indexIn( line ) != -1 )
3198  {
3199  name = nameRx.cap( 1 );
3200  }
3201  }
3202 
3203  //ignore lines until after "#"
3204  while ( !in.atEnd() && !line.startsWith( '#' ) )
3205  {
3206  line = in.readLine();
3207  }
3208  if ( in.atEnd() )
3209  {
3210  ok = false;
3211  return importedColors;
3212  }
3213 
3214  //ready to start reading colors
3215  QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
3216  while ( !in.atEnd() )
3217  {
3218  line = in.readLine();
3219  if ( rx.indexIn( line ) == -1 )
3220  {
3221  continue;
3222  }
3223  int red = rx.cap( 1 ).toInt();
3224  int green = rx.cap( 2 ).toInt();
3225  int blue = rx.cap( 3 ).toInt();
3226  QColor color = QColor( red, green, blue );
3227  if ( !color.isValid() )
3228  {
3229  continue;
3230  }
3231 
3232  //try to read color name
3233  QString label;
3234  if ( rx.captureCount() > 3 )
3235  {
3236  label = rx.cap( 4 ).simplified();
3237  }
3238  else
3239  {
3240  label = colorToName( color );
3241  }
3242 
3243  importedColors << qMakePair( color, label );
3244  }
3245 
3246  file.close();
3247  ok = true;
3248  return importedColors;
3249 }
3250 
3251 QColor QgsSymbolLayerV2Utils::parseColor( const QString& colorStr, bool strictEval )
3252 {
3253  bool hasAlpha;
3254  return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
3255 }
3256 
3257 QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString& colorStr, bool &containsAlpha, bool strictEval )
3258 {
3259  QColor parsedColor;
3260 
3261  //color in hex format "#aabbcc"
3262  if ( QColor::isValidColor( colorStr ) )
3263  {
3264  //string is a valid hex color string
3265  parsedColor.setNamedColor( colorStr );
3266  if ( parsedColor.isValid() )
3267  {
3268  containsAlpha = false;
3269  return parsedColor;
3270  }
3271  }
3272 
3273  //color in hex format, with alpha
3274  QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
3275  if ( hexColorAlphaRx.indexIn( colorStr ) != -1 )
3276  {
3277  QString hexColor = hexColorAlphaRx.cap( 1 );
3278  parsedColor.setNamedColor( QString( "#" ) + hexColor );
3279  bool alphaOk;
3280  int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
3281 
3282  if ( parsedColor.isValid() && alphaOk )
3283  {
3284  parsedColor.setAlpha( alphaHex );
3285  containsAlpha = true;
3286  return parsedColor;
3287  }
3288  }
3289 
3290  if ( !strictEval )
3291  {
3292  //color in hex format, without #
3293  QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
3294  if ( hexColorRx2.indexIn( colorStr ) != -1 )
3295  {
3296  //add "#" and parse
3297  parsedColor.setNamedColor( QString( "#" ) + colorStr );
3298  if ( parsedColor.isValid() )
3299  {
3300  containsAlpha = false;
3301  return parsedColor;
3302  }
3303  }
3304  }
3305 
3306  //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
3307  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*$" );
3308  if ( rgbFormatRx.indexIn( colorStr ) != -1 )
3309  {
3310  int r = rgbFormatRx.cap( 1 ).toInt();
3311  int g = rgbFormatRx.cap( 2 ).toInt();
3312  int b = rgbFormatRx.cap( 3 ).toInt();
3313  parsedColor.setRgb( r, g, b );
3314  if ( parsedColor.isValid() )
3315  {
3316  containsAlpha = false;
3317  return parsedColor;
3318  }
3319  }
3320 
3321  //color in (r%,g%,b%) format, brackets and rgb prefix optional
3322  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*$" );
3323  if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
3324  {
3325  int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3326  int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3327  int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3328  parsedColor.setRgb( r, g, b );
3329  if ( parsedColor.isValid() )
3330  {
3331  containsAlpha = false;
3332  return parsedColor;
3333  }
3334  }
3335 
3336  //color in (r,g,b,a) format, brackets and rgba prefix optional
3337  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*$" );
3338  if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
3339  {
3340  int r = rgbaFormatRx.cap( 1 ).toInt();
3341  int g = rgbaFormatRx.cap( 2 ).toInt();
3342  int b = rgbaFormatRx.cap( 3 ).toInt();
3343  int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
3344  parsedColor.setRgb( r, g, b, a );
3345  if ( parsedColor.isValid() )
3346  {
3347  containsAlpha = true;
3348  return parsedColor;
3349  }
3350  }
3351 
3352  //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
3353  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*$" );
3354  if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
3355  {
3356  int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3357  int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3358  int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3359  int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
3360  parsedColor.setRgb( r, g, b, a );
3361  if ( parsedColor.isValid() )
3362  {
3363  containsAlpha = true;
3364  return parsedColor;
3365  }
3366  }
3367 
3368  //couldn't parse string as color
3369  return QColor();
3370 }
3371 
3373 {
3374  switch ( u )
3375  {
3376  case QgsSymbolV2::MM:
3377  return c.scaleFactor();
3378  case QgsSymbolV2::MapUnit:
3379  {
3380  double mup = scale.computeMapUnitsPerPixel( c );
3381  if ( mup > 0 )
3382  {
3383  return 1.0 / mup;
3384  }
3385  else
3386  {
3387  return 1.0;
3388  }
3389  }
3390  case QgsSymbolV2::Pixel:
3391  return 1.0 / c.rasterScaleFactor();
3392  case QgsSymbolV2::Mixed:
3394  //no sensible value
3395  return 1.0;
3396  }
3397  return 1.0;
3398 }
3399 
3401 {
3402  double conversionFactor = lineWidthScaleFactor( c, unit, scale );
3403  double convertedSize = size * conversionFactor;
3404 
3405  if ( unit == QgsSymbolV2::MapUnit )
3406  {
3407  //check max/min size
3408  if ( scale.minSizeMMEnabled )
3409  convertedSize = qMax( convertedSize, scale.minSizeMM * c.scaleFactor() );
3410  if ( scale.maxSizeMMEnabled )
3411  convertedSize = qMin( convertedSize, scale.maxSizeMM * c.scaleFactor() );
3412  }
3413 
3414  return convertedSize;
3415 }
3416 
3418 {
3419  switch ( u )
3420  {
3421  case QgsSymbolV2::MM:
3422  return ( c.scaleFactor() * c.rasterScaleFactor() );
3423  case QgsSymbolV2::MapUnit:
3424  {
3425  double mup = scale.computeMapUnitsPerPixel( c );
3426  if ( mup > 0 )
3427  {
3428  return c.rasterScaleFactor() / mup;
3429  }
3430  else
3431  {
3432  return 1.0;
3433  }
3434  }
3435  case QgsSymbolV2::Pixel:
3436  return 1.0;
3437  case QgsSymbolV2::Mixed:
3439  //no sensible value
3440  return 1.0;
3441  }
3442  return 1.0;
3443 }
3444 
3446 {
3447  switch ( u )
3448  {
3449  case QgsSymbolV2::MM:
3450  return scale.computeMapUnitsPerPixel( c ) * c.scaleFactor() * c.rasterScaleFactor();
3451  case QgsSymbolV2::MapUnit:
3452  {
3453  return 1.0;
3454  }
3455  case QgsSymbolV2::Pixel:
3456  return scale.computeMapUnitsPerPixel( c );
3457  case QgsSymbolV2::Mixed:
3459  //no sensible value
3460  return 1.0;
3461  }
3462  return 1.0;
3463 }
3464 
3466 {
3467  QgsRenderContext context;
3468  context.setPainter( p );
3469  context.setRasterScaleFactor( 1.0 );
3470  if ( p && p->device() )
3471  {
3472  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
3473  }
3474  else
3475  {
3476  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
3477  }
3478  return context;
3479 }
3480 
3482 {
3483  if ( !image )
3484  {
3485  return;
3486  }
3487 
3488  QRgb myRgb;
3489  QImage::Format format = image->format();
3490  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
3491  {
3492  QgsDebugMsg( "no alpha channel." );
3493  return;
3494  }
3495 
3496  //change the alpha component of every pixel
3497  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
3498  {
3499  QRgb* scanLine = reinterpret_cast< QRgb* >( image->scanLine( heightIndex ) );
3500  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
3501  {
3502  myRgb = scanLine[widthIndex];
3503  if ( format == QImage::Format_ARGB32_Premultiplied )
3504  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3505  else
3506  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3507  }
3508  }
3509 }
3510 
3511 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, QRect rect, int radius, bool alphaOnly )
3512 {
3513  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
3514  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
3515  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
3516 
3517  if ( image.format() != QImage::Format_ARGB32_Premultiplied
3518  && image.format() != QImage::Format_RGB32 )
3519  {
3520  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
3521  }
3522 
3523  int r1 = rect.top();
3524  int r2 = rect.bottom();
3525  int c1 = rect.left();
3526  int c2 = rect.right();
3527 
3528  int bpl = image.bytesPerLine();
3529  int rgba[4];
3530  unsigned char* p;
3531 
3532  int i1 = 0;
3533  int i2 = 3;
3534 
3535  if ( alphaOnly ) // this seems to only work right for a black color
3536  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
3537 
3538  for ( int col = c1; col <= c2; col++ )
3539  {
3540  p = image.scanLine( r1 ) + col * 4;
3541  for ( int i = i1; i <= i2; i++ )
3542  rgba[i] = p[i] << 4;
3543 
3544  p += bpl;
3545  for ( int j = r1; j < r2; j++, p += bpl )
3546  for ( int i = i1; i <= i2; i++ )
3547  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3548  }
3549 
3550  for ( int row = r1; row <= r2; row++ )
3551  {
3552  p = image.scanLine( row ) + c1 * 4;
3553  for ( int i = i1; i <= i2; i++ )
3554  rgba[i] = p[i] << 4;
3555 
3556  p += 4;
3557  for ( int j = c1; j < c2; j++, p += 4 )
3558  for ( int i = i1; i <= i2; i++ )
3559  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3560  }
3561 
3562  for ( int col = c1; col <= c2; col++ )
3563  {
3564  p = image.scanLine( r2 ) + col * 4;
3565  for ( int i = i1; i <= i2; i++ )
3566  rgba[i] = p[i] << 4;
3567 
3568  p -= bpl;
3569  for ( int j = r1; j < r2; j++, p -= bpl )
3570  for ( int i = i1; i <= i2; i++ )
3571  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3572  }
3573 
3574  for ( int row = r1; row <= r2; row++ )
3575  {
3576  p = image.scanLine( row ) + c2 * 4;
3577  for ( int i = i1; i <= i2; i++ )
3578  rgba[i] = p[i] << 4;
3579 
3580  p -= 4;
3581  for ( int j = c1; j < c2; j++, p -= 4 )
3582  for ( int i = i1; i <= i2; i++ )
3583  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3584  }
3585 }
3586 
3588 {
3589  if ( alpha != 255 && alpha > 0 )
3590  {
3591  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3592  // where color values have to be premultiplied by alpha
3593  double alphaFactor = alpha / 255.;
3594  int r = 0, g = 0, b = 0;
3595  rgb.getRgb( &r, &g, &b );
3596 
3597  r *= alphaFactor;
3598  g *= alphaFactor;
3599  b *= alphaFactor;
3600  rgb.setRgb( r, g, b, alpha );
3601  }
3602  else if ( alpha == 0 )
3603  {
3604  rgb.setRgb( 0, 0, 0, 0 );
3605  }
3606 }
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 }
4046 
4048 {
4049  double scale = 1;
4050  bool roundToUnit = false;
4051  if ( unit == QgsSymbolV2::Mixed )
4052  {
4053  if ( props.contains( "uomScale" ) )
4054  {
4055  bool ok;
4056  scale = props.value( "uomScale" ).toDouble( &ok );
4057  if ( !ok )
4058  {
4059  return size;
4060  }
4061  }
4062  }
4063  else
4064  {
4065  if ( props.value( "uom" ) == "http://www.opengeospatial.org/se/units/metre" )
4066  {
4067  switch ( unit )
4068  {
4069  case QgsSymbolV2::MM:
4070  scale = 0.001;
4071  break;
4072  case QgsSymbolV2::Pixel:
4073  scale = 0.00028;
4074  roundToUnit = true;
4075  break;
4076  default:
4077  scale = 1;
4078  }
4079  }
4080  else
4081  {
4082  // target is pixels
4083  switch ( unit )
4084  {
4085  case QgsSymbolV2::MM:
4086  scale = 1 / 0.28;
4087  roundToUnit = true;
4088  break;
4089  // we don't have a good case for map units, as pixel values won't change based on zoom
4090  default:
4091  scale = 1;
4092  }
4093  }
4094 
4095  }
4096  double rescaled = size * scale;
4097  // round to unit if the result is pixels to avoid a weird looking SLD (people often think
4098  // of pixels as integers, even if SLD allows for float values in there
4099  if ( roundToUnit )
4100  {
4101  rescaled = qRound( rescaled );
4102  }
4103  return rescaled;
4104 }
4105 
4107 {
4108  double x = rescaleUom( point.x(), unit, props );
4109  double y = rescaleUom( point.y(), unit, props );
4110  return QPointF( x, y );
4111 }
4112 
4114 {
4115  QVector<qreal> result;
4117  for ( ; it != array.constEnd(); ++it )
4118  {
4119  result.append( rescaleUom( *it, unit, props ) );
4120  }
4121  return result;
4122 }
4123 
4125 {
4126  if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
4127  {
4128  QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
4129  scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
4130  ruleElem.appendChild( scaleMinDenomElem );
4131  }
4132 
4133  if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
4134  {
4135  QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
4136  scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
4137  ruleElem.appendChild( scaleMaxDenomElem );
4138  }
4139 }
4140 
4141 void QgsSymbolLayerV2Utils::mergeScaleDependencies( int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap& props )
4142 {
4143  if ( mScaleMinDenom != 0 )
4144  {
4145  bool ok;
4146  int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
4147  if ( !ok || parentScaleMinDenom <= 0 )
4148  props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
4149  else
4150  props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
4151  }
4152 
4153  if ( mScaleMaxDenom != 0 )
4154  {
4155  bool ok;
4156  int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
4157  if ( !ok || parentScaleMaxDenom <= 0 )
4158  props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
4159  else
4160  props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
4161  }
4162 }
QgsPolygon asPolygon() const
Return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list...
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
uchar * scanLine(int i)
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
static WkbType flatType(WkbType type)
Map 2d+ to 2d type.
Definition: qgis.cpp:399
static QString encodeSldLineJoinStyle(Qt::PenJoinStyle style)
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setForceVectorOutput(bool force)
static QgsSymbolV2::OutputUnit decodeSldUom(const QString &str, double *scaleFactor)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void clear()
void setLocked(bool locked)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
QDomNodeList elementsByTagName(const QString &tagname) const
QImage convertToFormat(Format format, QFlags< Qt::ImageConversionFlag > flags) const
static QgsSymbolV2Map loadSymbols(QDomElement &element)
QgsMultiPolyline asMultiPolyline() const
Return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
virtual NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
void setClipFeaturesToExtent(bool clipFeaturesToExtent)
Sets whether features drawn by the symbol should be clipped to the render context&#39;s extent...
Definition: qgssymbolv2.h:216
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
QString cap(int nth) const
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
QString & append(QChar ch)
static void multiplyImageOpacity(QImage *image, qreal alpha)
Multiplies opacity of image pixel values with a (global) transparency value.
QByteArray data(const QString &mimeType) const
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:62
static void drawStippledBackground(QPainter *painter, QRect rect)
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static Qt::BrushStyle decodeBrushStyle(const QString &str)
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
int width() const
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=nullptr, Qt::PenCapStyle *penCapStyle=nullptr, QVector< qreal > *customDashPattern=nullptr, double *dashOffset=nullptr)
static QIcon colorRampPreviewIcon(QgsVectorColorRampV2 *ramp, QSize size)
virtual QString type() const =0
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)
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, const QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
QString readLine(qint64 maxlen)
void append(const T &value)
void fill(const QColor &color)
static double rescaleUom(double size, QgsSymbolV2::OutputUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
int right() const
iterator begin()
QString name() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QString attribute(const QString &name, const QString &defValue) const
static double mapUnitScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> map units.
int length() const
QString nodeValue() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool hasFormat(const QString &mimeType) const
int weight() const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
void reserve(int alloc)
static QString encodeColor(const QColor &color)
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
static QString encodeSldUom(QgsSymbolV2::OutputUnit unit, double *scaleFactor)
QString attributeNS(const QString nsURI, const QString &localName, const QString &defValue) const
static void externalGraphicToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &mime, const QColor &color, double size=-1)
void setColorData(const QVariant &color)
static QgsStringMap getVendorOptionList(QDomElement &element)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the layer.
const_iterator constEnd() const
The output shall be in pixels.
Definition: qgssymbolv2.h:67
Calculate scale by the diameter.
Definition: qgssymbolv2.h:90
const T & at(int i) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
int size() const
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
static QVector< qreal > decodeRealVector(const QString &s)
static QColor colorFromMimeData(const QMimeData *data, bool &hasAlpha)
Attempts to parse mime data as a color.
QString simplified() const
static bool functionFromSldElement(QDomElement &element, QString &function)
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
QDomElement nextSiblingElement(const QString &tagName) const
static QString encodeSldFontStyle(QFont::Style style)
double computeMapUnitsPerPixel(const QgsRenderContext &c) const
Computes a map units per pixel scaling factor, respecting the minimum and maximum scales set for the ...
T value() const
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=nullptr)
Create ogr feature style string for pen.
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAlpha(int alpha)
virtual QgsStringMap properties() const =0
SymbolType type() const
Definition: qgssymbolv2.h:104
Line symbol.
Definition: qgssymbolv2.h:79
int height() const
static QPointF decodePoint(const QString &str)
static void mergeScaleDependencies(int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
static bool convertPolygonSymbolizerToPointMarker(QDomElement &element, QgsSymbolLayerV2List &layerList)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QgsSymbolLayerV2Registry * instance()
return the single instance of this class (instantiate it if not exists)
static QVector< qreal > decodeSldRealVector(const QString &s)
QDomElement documentElement() const
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static Qt::BrushStyle decodeSldBrushStyle(const QString &str)
QString join(const QString &separator) const
bool hasText() const
bool exists() const
static void createOnlineResourceElement(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format)
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
void drawLine(const QLineF &line)
static QStringList listSvgFilesAt(const QString &directory)
Return a list of svg files at the specified directory.
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
void setRgb(int r, int g, int b, int a)
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:335
void clear()
void chop(int n)
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
bool isValidColor(const QString &name)
QDomNodeList childNodes() const
static bool needMarkerLine(QDomElement &element)
QString parserErrorString() const
Returns parser error.
QChar separator()
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr)
Draw icon of the symbol that occupyies area given by size using the painter.
static bool needPointPatternFill(QDomElement &element)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double maxScale
The maximum scale, or 0.0 if unset.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:267
static QgsSymbolV2 * loadSymbol(const QDomElement &element)
Attempts to load a symbol from a DOM element.
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> pixel dimensions.
QgsPolyline asPolyline() const
Return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
Marker symbol.
Definition: qgssymbolv2.h:78
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
int size() const
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
void setMapUnitScale(const QgsMapUnitScale &scale)
QDomNode nextSibling() const
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
QDomElement toElement() const
static QString encodePenStyle(Qt::PenStyle style)
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
int indexIn(const QString &str, int offset, CaretMode caretMode) const
int renderingPass() const
bool isEmpty() const
T * data()
static QString symbolPathToName(QString path)
Get symbols&#39;s name from its path.
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void clear()
Mixed units in symbol layers.
Definition: qgssymbolv2.h:66
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:64
static QPainter::CompositionMode decodeBlendMode(const QString &s)
QString number(int n, int base)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
qreal x() const
qreal y() const
void append(const T &value)
static QString encodeSldFontWeight(int weight)
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
void setScaleFactor(double factor)
QString localName() const
void resize(int size)
bool atEnd() const
QString canonicalPath() const
static bool pointInPolygon(const QPolygonF &points, QPointF point)
Calculate whether a point is within of a QPolygonF.
QString text() const
QString text() const
QString path() const
static QgsSymbolLayerV2 * createFillLayerFromSld(QDomElement &element)
QgsGeometry * pointOnSurface() const
Returns a point within a geometry.
static QString encodePoint(QPointF point)
bool hasAttribute(const QString &name) const
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
static QPointF offsetPoint(QPointF pt, double angle, double dist)
static QString encodeSldAlpha(int alpha)
static bool needLinePatternFill(QDomElement &element)
static QgsPaintEffectRegistry * instance()
Returns a reference to the singleton instance of the paint effect registry.
The ouput shall be a percentage of another measurement (eg canvas size, feature size) ...
Definition: qgssymbolv2.h:68
int top() const
int captureCount() const
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
int red() const
static bool createExpressionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates a OGC Expression element based on the provided function expression.
void setPen(const QColor &color)
int width() const
void setRenderingPass(int renderingPass)
void setAttribute(const QString &name, const QString &value)
int left() const
static QStringList listSvgFiles()
Return a list of all available svg files.
QgsGeometry * buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
int toInt(bool *ok, int base) const
bool isEmpty() const
QDomNodeList elementsByTagName(const QString &tagname) const
bool isEmpty() const
QString trimmed() const
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QString symbolNameToPath(QString name)
Get symbol&#39;s path from its name.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
#define M_PI
The output shall be in map unitx.
Definition: qgssymbolv2.h:65
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:131
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
void setText(const QString &text)
void setPainter(QPainter *p)
static bool needFontMarker(QDomElement &element)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
static void saveProperties(QgsStringMap props, QDomDocument &doc, QDomElement &element)
virtual QgsStringMap properties() const =0
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
T & first()
QVector< QgsPolyline > QgsPolygon
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:50
iterator end()
static double estimateMaxSymbolBleed(QgsSymbolV2 *symbol)
Returns the maximum estimated bleed for the symbol.
static bool hasExternalGraphic(QDomElement &element)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString scheme() const
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:201
QVariant colorData() const
int alpha() const
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()
QString toLower() const
bool exists() const
static bool needSvgMarker(QDomElement &element)
static bool needSvgFill(QDomElement &element)
static bool geometryFromSldElement(QDomElement &element, QString &geomFunc)
static QgsSymbolLayerV2 * loadSymbolLayer(QDomElement &element)
bool contains(QChar ch, Qt::CaseSensitivity cs) const
static QgsSymbolLayerV2 * createLineLayerFromSld(QDomElement &element)
static QString encodeRealVector(const QVector< qreal > &v)
virtual QString layerType() const =0
Returns a string that represents this layer type.
QString expression() const
Return the original, unmodified expression string.
virtual void close()
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context&#39;s extent...
Definition: qgssymbolv2.h:226
bool isNull() const
virtual QgsSymbolV2 * subSymbol()
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
SymbolType
Type of the symbol.
Definition: qgssymbolv2.h:76
void setTexture(const QPixmap &pixmap)
int bytesPerLine() const
int blue() const
const T & at(int i) const
const_iterator constBegin() const
Contains information about the context of a rendering operation.
bool isValid() const
void save(QTextStream &str, int indent) const
static QDomElement createSvgParameterElement(QDomDocument &doc, const QString &name, const QString &value)
QDomNode firstChild() const
int width() const
QString mid(int position, int n) const
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static QString encodeBrushStyle(Qt::BrushStyle style)
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
static QColor parseColorWithAlpha(const QString &colorStr, bool &containsAlpha, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
static QPointF linesIntersection(QPointF p1, double t1, QPointF p2, double t2)
QgsGeometry * offsetCurve(double distance, int segments, int joinStyle, double mitreLimit) const
Returns an offset line at a given distance and side from an input line.
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:87
Struct for storing maximum and minimum scales for measurements in map units.
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
static QList< QColor > parseColorList(const QString &colorStr)
Attempts to parse a string as a list of colors using a variety of common formats, including hex codes...
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
bool isEmpty() const
QgsMultiPolygon asMultiPolygon() const
Return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
QStringList entryList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
QString family() const
static QMimeData * colorListToMimeData(const QgsNamedColorList &colorList, const bool allFormats=true)
Creates mime data from a list of named colors.
static bool lineInfo(QPointF p1, QPointF p2, double &angle, double &t)
static QDomElement expressionToOgcExpression(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates an OGC expression XML element.
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void setX(qreal x)
void setY(qreal y)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:381
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:80
static void labelTextToSld(QDomDocument &doc, QDomElement &element, const QString &label, const QFont &font, const QColor &color=QColor(), double size=-1)
int bottom() const
qreal & rx()
qreal & ry()
QDomComment createComment(const QString &value)
QStringList split(const QString &sep, const QString &str, bool allowEmptyEntries)
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QPicture symbolLayerPreviewPicture(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit units, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
Draws a symbol layer preview to a QPicture.
static QgsGeometry * fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
void push_back(const T &value)
static void clearSymbolMap(QgsSymbolV2Map &symbols)
static QStringList svgPaths()
Returns the pathes to svg directories.
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static Qt::PenCapStyle decodeSldLineCapStyle(const QString &str)
int height() const
static QColor decodeColor(const QString &str)
iterator insert(const Key &key, const T &value)
static QgsStringMap getSvgParameterList(QDomElement &element)
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
void setRasterScaleFactor(double factor)
Calculate scale by the area.
Definition: qgssymbolv2.h:89
QString tagName() const
static QgsGeometry * fromPolygon(const QgsPolygon &polygon)
Creates a new geometry from a QgsPolygon.
static int decodeSldFontWeight(const QString &str)
static int decodeSldAlpha(const QString &str)
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
void setData(const QString &mimeType, const QByteArray &data)
QgsSymbolLayerV2 * createSymbolLayer(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
create a new instance of symbol layer given symbol layer name and properties
QgsSymbolLayerV2 * createSymbolLayerFromSld(const QString &name, QDomElement &element) const
create a new instance of symbol layer given symbol layer name and SLD
static bool onlineResourceFromSldElement(QDomElement &element, QString &path, QString &format)
int size() const
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
uint length() const
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
static QgsStringMap parseProperties(QDomElement &element)
double rasterScaleFactor() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
virtual void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)=0
const_iterator constBegin() const
static bool hasWellKnownMark(QDomElement &element)
static QPointF polygonPointOnSurface(const QPolygonF &points)
Calculate a point within of a QPolygonF.
static Qt::PenJoinStyle decodeSldLineJoinStyle(const QString &str)
static QFont::Style decodeSldFontStyle(const QString &str)
int size() const
double scaleFactor() const
bool begin(QPaintDevice *device)
double minScale
The minimum scale, or 0.0 if unset.
int compare(const QString &other) const
static bool opacityFromSldElement(QDomElement &element, QString &alphaFunc)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
set layer&#39;s subsymbol. takes ownership of the passed symbol
iterator end()
Format format() const
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g.
static bool saveColorsToGpl(QFile &file, const QString &paletteName, const QgsNamedColorList &colors)
Exports colors to a gpl GIMP palette file.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the layer.
void setOutputUnit(QgsSymbolV2::OutputUnit u)
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:203
iterator begin()
T take(const Key &key)
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
static QString encodeSldLineCapStyle(Qt::PenCapStyle style)
static void premultiplyColor(QColor &rgb, int alpha)
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value.
QByteArray toByteArray(int indent) const
bool isValid() const
static void createOpacityElement(QDomDocument &doc, QDomElement &element, const QString &alphaFunc)
bool hasColor() const
static QgsNamedColorList colorListFromMimeData(const QMimeData *data)
Attempts to parse mime data as a list of named colors.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
Style style() const
bool isLocked() const
const T value(const Key &key) const
static bool needEllipseMarker(QDomElement &element)
bool isNull() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &borderColor=QColor(), double borderWidth=-1, double size=-1)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.