QGIS API Documentation  2.14.0-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 = 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  createFunctionElement( 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  createFunctionElement( 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  createFunctionElement( 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::expressionToOgcFilter( expr, doc );
2557  if ( !filterElem.isNull() )
2558  element.appendChild( filterElem );
2559  return true;
2560 }
2561 
2563 {
2564  QDomElement elem = element;
2565  if ( element.tagName() != "Filter" )
2566  {
2567  QDomNodeList filterNodes = element.elementsByTagName( "Filter" );
2568  if ( !filterNodes.isEmpty() )
2569  {
2570  elem = filterNodes.at( 0 ).toElement();
2571  }
2572  }
2573 
2574  if ( elem.isNull() )
2575  {
2576  return false;
2577  }
2578 
2579 
2581  if ( !expr )
2582  return false;
2583 
2584  bool valid = !expr->hasParserError();
2585  if ( !valid )
2586  {
2587  QgsDebugMsg( "parser error: " + expr->parserErrorString() );
2588  }
2589  else
2590  {
2591  function = expr->expression();
2592  }
2593 
2594  delete expr;
2595  return valid;
2596 }
2597 
2599  const QString& path, const QString& format )
2600 {
2601  // get resource url or relative path
2602  QString url = symbolPathToName( path );
2603  QDomElement onlineResourceElem = doc.createElement( "se:OnlineResource" );
2604  onlineResourceElem.setAttribute( "xlink:type", "simple" );
2605  onlineResourceElem.setAttribute( "xlink:href", url );
2606  element.appendChild( onlineResourceElem );
2607 
2608  QDomElement formatElem = doc.createElement( "se:Format" );
2609  formatElem.appendChild( doc.createTextNode( format ) );
2610  element.appendChild( formatElem );
2611 }
2612 
2614 {
2615  QgsDebugMsg( "Entered." );
2616 
2617  QDomElement onlineResourceElem = element.firstChildElement( "OnlineResource" );
2618  if ( onlineResourceElem.isNull() )
2619  return false;
2620 
2621  path = onlineResourceElem.attributeNS( "http://www.w3.org/1999/xlink", "href" );
2622 
2623  QDomElement formatElem = element.firstChildElement( "Format" );
2624  if ( formatElem.isNull() )
2625  return false; // OnlineResource requires a Format sibling element
2626 
2627  format = formatElem.firstChild().nodeValue();
2628  return true;
2629 }
2630 
2631 
2633 {
2634  QDomElement nodeElem = doc.createElement( "se:SvgParameter" );
2635  nodeElem.setAttribute( "name", name );
2636  nodeElem.appendChild( doc.createTextNode( value ) );
2637  return nodeElem;
2638 }
2639 
2641 {
2642  QgsStringMap params;
2643 
2644  QDomElement paramElem = element.firstChildElement();
2645  while ( !paramElem.isNull() )
2646  {
2647  if ( paramElem.localName() == "SvgParameter" || paramElem.localName() == "CssParameter" )
2648  {
2649  QString name = paramElem.attribute( "name" );
2650  QString value = paramElem.firstChild().nodeValue();
2651 
2652  if ( !name.isEmpty() && !value.isEmpty() )
2653  params[ name ] = value;
2654  }
2655 
2656  paramElem = paramElem.nextSiblingElement();
2657  }
2658 
2659  return params;
2660 }
2661 
2663 {
2664  QDomElement nodeElem = doc.createElement( "VendorOption" );
2665  nodeElem.setAttribute( "name", name );
2666  nodeElem.appendChild( doc.createTextNode( value ) );
2667  return nodeElem;
2668 }
2669 
2671 {
2672  QgsStringMap params;
2673 
2674  QDomElement paramElem = element.firstChildElement( "VendorOption" );
2675  while ( !paramElem.isNull() )
2676  {
2677  QString name = paramElem.attribute( "name" );
2678  QString value = paramElem.firstChild().nodeValue();
2679 
2680  if ( !name.isEmpty() && !value.isEmpty() )
2681  params[ name ] = value;
2682 
2683  paramElem = paramElem.nextSiblingElement( "VendorOption" );
2684  }
2685 
2686  return params;
2687 }
2688 
2689 
2691 {
2692  QgsStringMap props;
2693  QDomElement e = element.firstChildElement();
2694  while ( !e.isNull() )
2695  {
2696  if ( e.tagName() != "prop" )
2697  {
2698  QgsDebugMsg( "unknown tag " + e.tagName() );
2699  }
2700  else
2701  {
2702  QString propKey = e.attribute( "k" );
2703  QString propValue = e.attribute( "v" );
2704  props[propKey] = propValue;
2705  }
2706  e = e.nextSiblingElement();
2707  }
2708  return props;
2709 }
2710 
2711 
2713 {
2714  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
2715  {
2716  QDomElement propEl = doc.createElement( "prop" );
2717  propEl.setAttribute( "k", it.key() );
2718  propEl.setAttribute( "v", it.value() );
2719  element.appendChild( propEl );
2720  }
2721 }
2722 
2724 {
2725  // go through symbols one-by-one and load them
2726 
2727  QgsSymbolV2Map symbols;
2728  QDomElement e = element.firstChildElement();
2729 
2730  while ( !e.isNull() )
2731  {
2732  if ( e.tagName() == "symbol" )
2733  {
2735  if ( symbol )
2736  symbols.insert( e.attribute( "name" ), symbol );
2737  }
2738  else
2739  {
2740  QgsDebugMsg( "unknown tag: " + e.tagName() );
2741  }
2742  e = e.nextSiblingElement();
2743  }
2744 
2745 
2746  // now walk through the list of symbols and find those prefixed with @
2747  // these symbols are sub-symbols of some other symbol layers
2748  // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo"
2749  QStringList subsymbols;
2750 
2751  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2752  {
2753  if ( it.key()[0] != '@' )
2754  continue;
2755 
2756  // add to array (for deletion)
2757  subsymbols.append( it.key() );
2758 
2759  QStringList parts = it.key().split( '@' );
2760  if ( parts.count() < 3 )
2761  {
2762  QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
2763  delete it.value(); // we must delete it
2764  continue; // some invalid syntax
2765  }
2766  QString symname = parts[1];
2767  int symlayer = parts[2].toInt();
2768 
2769  if ( !symbols.contains( symname ) )
2770  {
2771  QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
2772  delete it.value(); // we must delete it
2773  continue;
2774  }
2775 
2776  QgsSymbolV2* sym = symbols[symname];
2777  if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
2778  {
2779  QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
2780  delete it.value(); // we must delete it
2781  continue;
2782  }
2783 
2784  // set subsymbol takes ownership
2785  bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
2786  if ( !res )
2787  {
2788  QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
2789  }
2790 
2791 
2792  }
2793 
2794  // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
2795  for ( int i = 0; i < subsymbols.count(); i++ )
2796  symbols.take( subsymbols[i] );
2797 
2798  return symbols;
2799 }
2800 
2802 {
2803  QDomElement symbolsElem = doc.createElement( tagName );
2804 
2805  // save symbols
2806  for ( QMap<QString, QgsSymbolV2*>::iterator its = symbols.begin(); its != symbols.end(); ++its )
2807  {
2808  QDomElement symEl = saveSymbol( its.key(), its.value(), doc );
2809  symbolsElem.appendChild( symEl );
2810  }
2811 
2812  return symbolsElem;
2813 }
2814 
2816 {
2817  qDeleteAll( symbols );
2818  symbols.clear();
2819 }
2820 
2821 
2823 {
2824  QString rampType = element.attribute( "type" );
2825 
2826  // parse properties
2828 
2829  if ( rampType == "gradient" )
2830  return QgsVectorGradientColorRampV2::create( props );
2831  else if ( rampType == "random" )
2832  return QgsVectorRandomColorRampV2::create( props );
2833  else if ( rampType == "colorbrewer" )
2835  else if ( rampType == "cpt-city" )
2836  return QgsCptCityColorRampV2::create( props );
2837  else
2838  {
2839  QgsDebugMsg( "unknown colorramp type " + rampType );
2840  return nullptr;
2841  }
2842 }
2843 
2844 
2846 {
2847  QDomElement rampEl = doc.createElement( "colorramp" );
2848  rampEl.setAttribute( "type", ramp->type() );
2849  rampEl.setAttribute( "name", name );
2850 
2851  QgsSymbolLayerV2Utils::saveProperties( ramp->properties(), doc, rampEl );
2852  return rampEl;
2853 }
2854 
2856 {
2857  if ( !color.isValid() )
2858  {
2859  return QString();
2860  }
2861 
2862  //TODO - utilise a color names database (such as X11) to return nicer names
2863  //for now, just return hex codes
2864  return color.name();
2865 }
2866 
2868 {
2869  QList<QColor> colors;
2870 
2871  //try splitting string at commas, spaces or newlines
2872  QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
2873  QStringList::iterator it = components.begin();
2874  for ( ; it != components.end(); ++it )
2875  {
2876  QColor result = parseColor( *it, true );
2877  if ( result.isValid() )
2878  {
2879  colors << result;
2880  }
2881  }
2882  if ( colors.length() > 0 )
2883  {
2884  return colors;
2885  }
2886 
2887  //try splitting string at commas or newlines
2888  components = colorStr.split( QRegExp( "(,|\n)" ) );
2889  it = components.begin();
2890  for ( ; it != components.end(); ++it )
2891  {
2892  QColor result = parseColor( *it, true );
2893  if ( result.isValid() )
2894  {
2895  colors << result;
2896  }
2897  }
2898  if ( colors.length() > 0 )
2899  {
2900  return colors;
2901  }
2902 
2903  //try splitting string at whitespace or newlines
2904  components = colorStr.simplified().split( QString( ' ' ) );
2905  it = components.begin();
2906  for ( ; it != components.end(); ++it )
2907  {
2908  QColor result = parseColor( *it, true );
2909  if ( result.isValid() )
2910  {
2911  colors << result;
2912  }
2913  }
2914  if ( colors.length() > 0 )
2915  {
2916  return colors;
2917  }
2918 
2919  //try splitting string just at newlines
2920  components = colorStr.split( '\n' );
2921  it = components.begin();
2922  for ( ; it != components.end(); ++it )
2923  {
2924  QColor result = parseColor( *it, true );
2925  if ( result.isValid() )
2926  {
2927  colors << result;
2928  }
2929  }
2930 
2931  return colors;
2932 }
2933 
2935 {
2936  //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
2937  //value, and can be used when pasting colors outside of QGIS).
2938  QMimeData *mimeData = new QMimeData;
2939  mimeData->setColorData( QVariant( color ) );
2940  mimeData->setText( color.name() );
2941  return mimeData;
2942 }
2943 
2944 QColor QgsSymbolLayerV2Utils::colorFromMimeData( const QMimeData * mimeData, bool& hasAlpha )
2945 {
2946  //attempt to read color data directly from mime
2947  QColor mimeColor = mimeData->colorData().value<QColor>();
2948  if ( mimeColor.isValid() )
2949  {
2950  hasAlpha = true;
2951  return mimeColor;
2952  }
2953 
2954  //attempt to intrepret a color from mime text data
2955  hasAlpha = false;
2956  QColor textColor = QgsSymbolLayerV2Utils::parseColorWithAlpha( mimeData->text(), hasAlpha );
2957  if ( textColor.isValid() )
2958  {
2959  return textColor;
2960  }
2961 
2962  //could not get color from mime data
2963  return QColor();
2964 }
2965 
2967 {
2968  QgsNamedColorList mimeColors;
2969 
2970  //prefer xml format
2971  if ( data->hasFormat( "text/xml" ) )
2972  {
2973  //get XML doc
2974  QByteArray encodedData = data->data( "text/xml" );
2975  QDomDocument xmlDoc;
2976  xmlDoc.setContent( encodedData );
2977 
2978  QDomElement dragDataElem = xmlDoc.documentElement();
2979  if ( dragDataElem.tagName() == "ColorSchemeModelDragData" )
2980  {
2981  QDomNodeList nodeList = dragDataElem.childNodes();
2982  int nChildNodes = nodeList.size();
2983  QDomElement currentElem;
2984 
2985  for ( int i = 0; i < nChildNodes; ++i )
2986  {
2987  currentElem = nodeList.at( i ).toElement();
2988  if ( currentElem.isNull() )
2989  {
2990  continue;
2991  }
2992 
2993  QPair< QColor, QString> namedColor;
2994  namedColor.first = QgsSymbolLayerV2Utils::decodeColor( currentElem.attribute( "color", "255,255,255,255" ) );
2995  namedColor.second = currentElem.attribute( "label", "" );
2996 
2997  mimeColors << namedColor;
2998  }
2999  }
3000  }
3001 
3002  if ( mimeColors.length() == 0 && data->hasFormat( "application/x-colorobject-list" ) )
3003  {
3004  //get XML doc
3005  QByteArray encodedData = data->data( "application/x-colorobject-list" );
3006  QDomDocument xmlDoc;
3007  xmlDoc.setContent( encodedData );
3008 
3009  QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QString( "colors" ) );
3010  if ( colorsNodes.length() > 0 )
3011  {
3012  QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
3013  QDomNodeList colorNodeList = colorsElem.childNodes();
3014  int nChildNodes = colorNodeList.size();
3015  QDomElement currentElem;
3016 
3017  for ( int i = 0; i < nChildNodes; ++i )
3018  {
3019  //li element
3020  currentElem = colorNodeList.at( i ).toElement();
3021  if ( currentElem.isNull() )
3022  {
3023  continue;
3024  }
3025 
3026  QDomNodeList colorNodes = currentElem.elementsByTagName( QString( "color" ) );
3027  QDomNodeList nameNodes = currentElem.elementsByTagName( QString( "name" ) );
3028 
3029  if ( colorNodes.length() > 0 )
3030  {
3031  QDomElement colorElem = colorNodes.at( 0 ).toElement();
3032 
3033  QStringList colorParts = colorElem.text().simplified().split( ' ' );
3034  if ( colorParts.length() < 3 )
3035  {
3036  continue;
3037  }
3038 
3039  int red = colorParts.at( 0 ).toDouble() * 255;
3040  int green = colorParts.at( 1 ).toDouble() * 255;
3041  int blue = colorParts.at( 2 ).toDouble() * 255;
3042  QPair< QColor, QString> namedColor;
3043  namedColor.first = QColor( red, green, blue );
3044  if ( nameNodes.length() > 0 )
3045  {
3046  QDomElement nameElem = nameNodes.at( 0 ).toElement();
3047  namedColor.second = nameElem.text();
3048  }
3049  mimeColors << namedColor;
3050  }
3051  }
3052  }
3053  }
3054 
3055  if ( mimeColors.length() == 0 && data->hasText() )
3056  {
3057  //attempt to read color data from mime text
3059  QList< QColor >::iterator it = parsedColors.begin();
3060  for ( ; it != parsedColors.end(); ++it )
3061  {
3062  mimeColors << qMakePair( *it, QString() );
3063  }
3064  }
3065 
3066  if ( mimeColors.length() == 0 && data->hasColor() )
3067  {
3068  //attempt to read color data directly from mime
3069  QColor mimeColor = data->colorData().value<QColor>();
3070  if ( mimeColor.isValid() )
3071  {
3072  mimeColors << qMakePair( mimeColor, QString() );
3073  }
3074  }
3075 
3076  return mimeColors;
3077 }
3078 
3080 {
3081  //native format
3082  QMimeData* mimeData = new QMimeData();
3083  QDomDocument xmlDoc;
3084  QDomElement xmlRootElement = xmlDoc.createElement( "ColorSchemeModelDragData" );
3085  xmlDoc.appendChild( xmlRootElement );
3086 
3087  QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
3088  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3089  {
3090  QDomElement namedColor = xmlDoc.createElement( "NamedColor" );
3091  namedColor.setAttribute( "color", QgsSymbolLayerV2Utils::encodeColor(( *colorIt ).first ) );
3092  namedColor.setAttribute( "label", ( *colorIt ).second );
3093  xmlRootElement.appendChild( namedColor );
3094  }
3095  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
3096 
3097  if ( !allFormats )
3098  {
3099  return mimeData;
3100  }
3101 
3102  //set mime text to list of hex values
3103  colorIt = colorList.constBegin();
3104  QStringList colorListString;
3105  for ( ; colorIt != colorList.constEnd(); ++colorIt )
3106  {
3107  colorListString << ( *colorIt ).first.name();
3108  }
3109  mimeData->setText( colorListString.join( "\n" ) );
3110 
3111  //set mime color data to first color
3112  if ( colorList.length() > 0 )
3113  {
3114  mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
3115  }
3116 
3117  return mimeData;
3118 }
3119 
3120 bool QgsSymbolLayerV2Utils::saveColorsToGpl( QFile &file, const QString& paletteName, const QgsNamedColorList& colors )
3121 {
3122  if ( !file.open( QIODevice::ReadWrite ) )
3123  {
3124  return false;
3125  }
3126 
3127  QTextStream stream( &file );
3128  stream << "GIMP Palette" << endl;
3129  if ( paletteName.isEmpty() )
3130  {
3131  stream << "Name: QGIS Palette" << endl;
3132  }
3133  else
3134  {
3135  stream << "Name: " << paletteName << endl;
3136  }
3137  stream << "Columns: 4" << endl;
3138  stream << '#' << endl;
3139 
3140  for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
3141  {
3142  QColor color = ( *colorIt ).first;
3143  if ( !color.isValid() )
3144  {
3145  continue;
3146  }
3147  stream << QString( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
3148  stream << "\t" << (( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
3149  }
3150  file.close();
3151 
3152  return true;
3153 }
3154 
3156 {
3157  QgsNamedColorList importedColors;
3158 
3159  if ( !file.open( QIODevice::ReadOnly ) )
3160  {
3161  ok = false;
3162  return importedColors;
3163  }
3164 
3165  QTextStream in( &file );
3166 
3167  QString line = in.readLine();
3168  if ( !line.startsWith( "GIMP Palette" ) )
3169  {
3170  ok = false;
3171  return importedColors;
3172  }
3173 
3174  //find name line
3175  while ( !in.atEnd() && !line.startsWith( "Name:" ) && !line.startsWith( '#' ) )
3176  {
3177  line = in.readLine();
3178  }
3179  if ( line.startsWith( "Name:" ) )
3180  {
3181  QRegExp nameRx( "Name:\\s*(\\S.*)$" );
3182  if ( nameRx.indexIn( line ) != -1 )
3183  {
3184  name = nameRx.cap( 1 );
3185  }
3186  }
3187 
3188  //ignore lines until after "#"
3189  while ( !in.atEnd() && !line.startsWith( '#' ) )
3190  {
3191  line = in.readLine();
3192  }
3193  if ( in.atEnd() )
3194  {
3195  ok = false;
3196  return importedColors;
3197  }
3198 
3199  //ready to start reading colors
3200  QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
3201  while ( !in.atEnd() )
3202  {
3203  line = in.readLine();
3204  if ( rx.indexIn( line ) == -1 )
3205  {
3206  continue;
3207  }
3208  int red = rx.cap( 1 ).toInt();
3209  int green = rx.cap( 2 ).toInt();
3210  int blue = rx.cap( 3 ).toInt();
3211  QColor color = QColor( red, green, blue );
3212  if ( !color.isValid() )
3213  {
3214  continue;
3215  }
3216 
3217  //try to read color name
3218  QString label;
3219  if ( rx.captureCount() > 3 )
3220  {
3221  label = rx.cap( 4 ).simplified();
3222  }
3223  else
3224  {
3225  label = colorToName( color );
3226  }
3227 
3228  importedColors << qMakePair( color, label );
3229  }
3230 
3231  file.close();
3232  ok = true;
3233  return importedColors;
3234 }
3235 
3236 QColor QgsSymbolLayerV2Utils::parseColor( const QString& colorStr, bool strictEval )
3237 {
3238  bool hasAlpha;
3239  return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
3240 }
3241 
3242 QColor QgsSymbolLayerV2Utils::parseColorWithAlpha( const QString& colorStr, bool &containsAlpha, bool strictEval )
3243 {
3244  QColor parsedColor;
3245 
3246  //color in hex format "#aabbcc"
3247  if ( QColor::isValidColor( colorStr ) )
3248  {
3249  //string is a valid hex color string
3250  parsedColor.setNamedColor( colorStr );
3251  if ( parsedColor.isValid() )
3252  {
3253  containsAlpha = false;
3254  return parsedColor;
3255  }
3256  }
3257 
3258  //color in hex format, with alpha
3259  QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
3260  if ( hexColorAlphaRx.indexIn( colorStr ) != -1 )
3261  {
3262  QString hexColor = hexColorAlphaRx.cap( 1 );
3263  parsedColor.setNamedColor( QString( "#" ) + hexColor );
3264  bool alphaOk;
3265  int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
3266 
3267  if ( parsedColor.isValid() && alphaOk )
3268  {
3269  parsedColor.setAlpha( alphaHex );
3270  containsAlpha = true;
3271  return parsedColor;
3272  }
3273  }
3274 
3275  if ( !strictEval )
3276  {
3277  //color in hex format, without #
3278  QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
3279  if ( hexColorRx2.indexIn( colorStr ) != -1 )
3280  {
3281  //add "#" and parse
3282  parsedColor.setNamedColor( QString( "#" ) + colorStr );
3283  if ( parsedColor.isValid() )
3284  {
3285  containsAlpha = false;
3286  return parsedColor;
3287  }
3288  }
3289  }
3290 
3291  //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
3292  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*$" );
3293  if ( rgbFormatRx.indexIn( colorStr ) != -1 )
3294  {
3295  int r = rgbFormatRx.cap( 1 ).toInt();
3296  int g = rgbFormatRx.cap( 2 ).toInt();
3297  int b = rgbFormatRx.cap( 3 ).toInt();
3298  parsedColor.setRgb( r, g, b );
3299  if ( parsedColor.isValid() )
3300  {
3301  containsAlpha = false;
3302  return parsedColor;
3303  }
3304  }
3305 
3306  //color in (r%,g%,b%) format, brackets and rgb prefix optional
3307  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*$" );
3308  if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
3309  {
3310  int r = qRound( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3311  int g = qRound( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3312  int b = qRound( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
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,a) format, brackets and rgba prefix optional
3322  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*$" );
3323  if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
3324  {
3325  int r = rgbaFormatRx.cap( 1 ).toInt();
3326  int g = rgbaFormatRx.cap( 2 ).toInt();
3327  int b = rgbaFormatRx.cap( 3 ).toInt();
3328  int a = qRound( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
3329  parsedColor.setRgb( r, g, b, a );
3330  if ( parsedColor.isValid() )
3331  {
3332  containsAlpha = true;
3333  return parsedColor;
3334  }
3335  }
3336 
3337  //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
3338  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*$" );
3339  if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
3340  {
3341  int r = qRound( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
3342  int g = qRound( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
3343  int b = qRound( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
3344  int a = qRound( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
3345  parsedColor.setRgb( r, g, b, a );
3346  if ( parsedColor.isValid() )
3347  {
3348  containsAlpha = true;
3349  return parsedColor;
3350  }
3351  }
3352 
3353  //couldn't parse string as color
3354  return QColor();
3355 }
3356 
3358 {
3359  switch ( u )
3360  {
3361  case QgsSymbolV2::MM:
3362  return c.scaleFactor();
3363  case QgsSymbolV2::MapUnit:
3364  {
3365  double mup = scale.computeMapUnitsPerPixel( c );
3366  if ( mup > 0 )
3367  {
3368  return 1.0 / mup;
3369  }
3370  else
3371  {
3372  return 1.0;
3373  }
3374  }
3375  case QgsSymbolV2::Pixel:
3376  return 1.0 / c.rasterScaleFactor();
3377  case QgsSymbolV2::Mixed:
3379  //no sensible value
3380  return 1.0;
3381  }
3382  return 1.0;
3383 }
3384 
3386 {
3387  double conversionFactor = lineWidthScaleFactor( c, unit, scale );
3388  double convertedSize = size * conversionFactor;
3389 
3390  if ( unit == QgsSymbolV2::MapUnit )
3391  {
3392  //check max/min size
3393  if ( scale.minSizeMMEnabled )
3394  convertedSize = qMax( convertedSize, scale.minSizeMM * c.scaleFactor() );
3395  if ( scale.maxSizeMMEnabled )
3396  convertedSize = qMin( convertedSize, scale.maxSizeMM * c.scaleFactor() );
3397  }
3398 
3399  return convertedSize;
3400 }
3401 
3403 {
3404  switch ( u )
3405  {
3406  case QgsSymbolV2::MM:
3407  return ( c.scaleFactor() * c.rasterScaleFactor() );
3408  case QgsSymbolV2::MapUnit:
3409  {
3410  double mup = scale.computeMapUnitsPerPixel( c );
3411  if ( mup > 0 )
3412  {
3413  return c.rasterScaleFactor() / mup;
3414  }
3415  else
3416  {
3417  return 1.0;
3418  }
3419  }
3420  case QgsSymbolV2::Pixel:
3421  return 1.0;
3422  case QgsSymbolV2::Mixed:
3424  //no sensible value
3425  return 1.0;
3426  }
3427  return 1.0;
3428 }
3429 
3431 {
3432  switch ( u )
3433  {
3434  case QgsSymbolV2::MM:
3435  return scale.computeMapUnitsPerPixel( c ) * c.scaleFactor() * c.rasterScaleFactor();
3436  case QgsSymbolV2::MapUnit:
3437  {
3438  return 1.0;
3439  }
3440  case QgsSymbolV2::Pixel:
3441  return scale.computeMapUnitsPerPixel( c );
3442  case QgsSymbolV2::Mixed:
3444  //no sensible value
3445  return 1.0;
3446  }
3447  return 1.0;
3448 }
3449 
3451 {
3452  QgsRenderContext context;
3453  context.setPainter( p );
3454  context.setRasterScaleFactor( 1.0 );
3455  if ( p && p->device() )
3456  {
3457  context.setScaleFactor( p->device()->logicalDpiX() / 25.4 );
3458  }
3459  else
3460  {
3461  context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
3462  }
3463  return context;
3464 }
3465 
3467 {
3468  if ( !image )
3469  {
3470  return;
3471  }
3472 
3473  QRgb myRgb;
3474  QImage::Format format = image->format();
3475  if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
3476  {
3477  QgsDebugMsg( "no alpha channel." );
3478  return;
3479  }
3480 
3481  //change the alpha component of every pixel
3482  for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
3483  {
3484  QRgb* scanLine = reinterpret_cast< QRgb* >( image->scanLine( heightIndex ) );
3485  for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
3486  {
3487  myRgb = scanLine[widthIndex];
3488  if ( format == QImage::Format_ARGB32_Premultiplied )
3489  scanLine[widthIndex] = qRgba( alpha * qRed( myRgb ), alpha * qGreen( myRgb ), alpha * qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3490  else
3491  scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), alpha * qAlpha( myRgb ) );
3492  }
3493  }
3494 }
3495 
3496 void QgsSymbolLayerV2Utils::blurImageInPlace( QImage& image, QRect rect, int radius, bool alphaOnly )
3497 {
3498  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
3499  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
3500  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
3501 
3502  if ( image.format() != QImage::Format_ARGB32_Premultiplied
3503  && image.format() != QImage::Format_RGB32 )
3504  {
3505  image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
3506  }
3507 
3508  int r1 = rect.top();
3509  int r2 = rect.bottom();
3510  int c1 = rect.left();
3511  int c2 = rect.right();
3512 
3513  int bpl = image.bytesPerLine();
3514  int rgba[4];
3515  unsigned char* p;
3516 
3517  int i1 = 0;
3518  int i2 = 3;
3519 
3520  if ( alphaOnly ) // this seems to only work right for a black color
3521  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
3522 
3523  for ( int col = c1; col <= c2; col++ )
3524  {
3525  p = image.scanLine( r1 ) + col * 4;
3526  for ( int i = i1; i <= i2; i++ )
3527  rgba[i] = p[i] << 4;
3528 
3529  p += bpl;
3530  for ( int j = r1; j < r2; j++, p += bpl )
3531  for ( int i = i1; i <= i2; i++ )
3532  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3533  }
3534 
3535  for ( int row = r1; row <= r2; row++ )
3536  {
3537  p = image.scanLine( row ) + c1 * 4;
3538  for ( int i = i1; i <= i2; i++ )
3539  rgba[i] = p[i] << 4;
3540 
3541  p += 4;
3542  for ( int j = c1; j < c2; j++, p += 4 )
3543  for ( int i = i1; i <= i2; i++ )
3544  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3545  }
3546 
3547  for ( int col = c1; col <= c2; col++ )
3548  {
3549  p = image.scanLine( r2 ) + col * 4;
3550  for ( int i = i1; i <= i2; i++ )
3551  rgba[i] = p[i] << 4;
3552 
3553  p -= bpl;
3554  for ( int j = r1; j < r2; j++, p -= bpl )
3555  for ( int i = i1; i <= i2; i++ )
3556  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3557  }
3558 
3559  for ( int row = r1; row <= r2; row++ )
3560  {
3561  p = image.scanLine( row ) + c2 * 4;
3562  for ( int i = i1; i <= i2; i++ )
3563  rgba[i] = p[i] << 4;
3564 
3565  p -= 4;
3566  for ( int j = c1; j < c2; j++, p -= 4 )
3567  for ( int i = i1; i <= i2; i++ )
3568  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
3569  }
3570 }
3571 
3573 {
3574  if ( alpha != 255 && alpha > 0 )
3575  {
3576  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3577  // where color values have to be premultiplied by alpha
3578  double alphaFactor = alpha / 255.;
3579  int r = 0, g = 0, b = 0;
3580  rgb.getRgb( &r, &g, &b );
3581 
3582  r *= alphaFactor;
3583  g *= alphaFactor;
3584  b *= alphaFactor;
3585  rgb.setRgb( r, g, b, alpha );
3586  }
3587  else if ( alpha == 0 )
3588  {
3589  rgb.setRgb( 0, 0, 0, 0 );
3590  }
3591 }
3592 
3593 #if 0
3594 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
3595 {
3596  switch ( lhs.type() )
3597  {
3598  case QVariant::Int:
3599  return lhs.toInt() < rhs.toInt();
3600  case QVariant::UInt:
3601  return lhs.toUInt() < rhs.toUInt();
3602  case QVariant::LongLong:
3603  return lhs.toLongLong() < rhs.toLongLong();
3604  case QVariant::ULongLong:
3605  return lhs.toULongLong() < rhs.toULongLong();
3606  case QVariant::Double:
3607  return lhs.toDouble() < rhs.toDouble();
3608  case QVariant::Char:
3609  return lhs.toChar() < rhs.toChar();
3610  case QVariant::Date:
3611  return lhs.toDate() < rhs.toDate();
3612  case QVariant::Time:
3613  return lhs.toTime() < rhs.toTime();
3614  case QVariant::DateTime:
3615  return lhs.toDateTime() < rhs.toDateTime();
3616  default:
3617  return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
3618  }
3619 }
3620 
3621 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
3622 {
3623  return ! _QVariantLessThan( lhs, rhs );
3624 }
3625 #endif
3626 
3628 {
3629  if ( order == Qt::AscendingOrder )
3630  {
3631  //qSort( list.begin(), list.end(), _QVariantLessThan );
3632  qSort( list.begin(), list.end(), qgsVariantLessThan );
3633  }
3634  else // Qt::DescendingOrder
3635  {
3636  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
3637  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
3638  }
3639 }
3640 
3641 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance )
3642 {
3643  double dx = directionPoint.x() - startPoint.x();
3644  double dy = directionPoint.y() - startPoint.y();
3645  double length = sqrt( dx * dx + dy * dy );
3646  double scaleFactor = distance / length;
3647  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
3648 }
3649 
3650 
3652 {
3653  // copied from QgsMarkerCatalogue - TODO: unify
3654  QStringList list;
3656 
3657  for ( int i = 0; i < svgPaths.size(); i++ )
3658  {
3659  QDir dir( svgPaths[i] );
3660  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3661  {
3662  svgPaths.insert( i + 1, dir.path() + '/' + item );
3663  }
3664 
3665  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3666  {
3667  // TODO test if it is correct SVG
3668  list.append( dir.path() + '/' + item );
3669  }
3670  }
3671  return list;
3672 }
3673 
3674 // Stripped down version of listSvgFiles() for specified directory
3676 {
3677  // TODO anything that applies for the listSvgFiles() applies this also
3678 
3679  QStringList list;
3680  QStringList svgPaths;
3681  svgPaths.append( directory );
3682 
3683  for ( int i = 0; i < svgPaths.size(); i++ )
3684  {
3685  QDir dir( svgPaths[i] );
3686  Q_FOREACH ( const QString& item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3687  {
3688  svgPaths.insert( i + 1, dir.path() + '/' + item );
3689  }
3690 
3691  Q_FOREACH ( const QString& item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3692  {
3693  list.append( dir.path() + '/' + item );
3694  }
3695  }
3696  return list;
3697 
3698 }
3699 
3701 {
3702  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
3703 
3704  // we might have a full path...
3705  if ( QFile( name ).exists() )
3706  return QFileInfo( name ).canonicalFilePath();
3707 
3708  // or it might be an url...
3709  if ( name.contains( "://" ) )
3710  {
3711  QUrl url( name );
3712  if ( url.isValid() && !url.scheme().isEmpty() )
3713  {
3714  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
3715  {
3716  // it's a url to a local file
3717  name = url.toLocalFile();
3718  if ( QFile( name ).exists() )
3719  {
3720  return QFileInfo( name ).canonicalFilePath();
3721  }
3722  }
3723  else
3724  {
3725  // it's a url pointing to a online resource
3726  return name;
3727  }
3728  }
3729  }
3730 
3731  // SVG symbol not found - probably a relative path was used
3732 
3734  for ( int i = 0; i < svgPaths.size(); i++ )
3735  {
3736  QString svgPath = svgPaths[i];
3737  if ( svgPath.endsWith( QChar( '/' ) ) )
3738  {
3739  svgPath.chop( 1 );
3740  }
3741 
3742  QgsDebugMsg( "SvgPath: " + svgPath );
3743  // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
3744  //QFileInfo myInfo( name );
3745  //QString myFileName = myInfo.fileName(); // foo.svg
3746  //QString myLowestDir = myInfo.dir().dirName();
3747  //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : '/' + myLowestDir ) + '/' + myFileName;
3748  QString myLocalPath = svgPath + QDir::separator() + name;
3749 
3750  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
3751  if ( QFile( myLocalPath ).exists() )
3752  {
3753  QgsDebugMsg( "Svg found in alternative path" );
3754  return QFileInfo( myLocalPath ).canonicalFilePath();
3755  }
3756  }
3757 
3758  QFileInfo pfi( QgsProject::instance()->fileName() );
3759  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
3760  if ( pfi.exists() && QFile( alternatePath ).exists() )
3761  {
3762  QgsDebugMsg( "Svg found in alternative path" );
3763  return QFileInfo( alternatePath ).canonicalFilePath();
3764  }
3765  else
3766  {
3767  QgsDebugMsg( "Svg not found in project path" );
3768  }
3769  //couldnt find the file, no happy ending :-(
3770  QgsDebugMsg( "Computed alternate path but no svg there either" );
3771 
3772  return QString();
3773 }
3774 
3776 {
3777  // copied from QgsSymbol::writeXML
3778 
3779  QFileInfo fi( path );
3780  if ( !fi.exists() )
3781  return path;
3782 
3783  path = fi.canonicalFilePath();
3784 
3786 
3787  bool isInSvgPathes = false;
3788  for ( int i = 0; i < svgPaths.size(); i++ )
3789  {
3790  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
3791 
3792  if ( !dir.isEmpty() && path.startsWith( dir ) )
3793  {
3794  path = path.mid( dir.size() + 1 );
3795  isInSvgPathes = true;
3796  break;
3797  }
3798  }
3799 
3800  if ( isInSvgPathes )
3801  return path;
3802 
3803  return QgsProject::instance()->writePath( path );
3804 }
3805 
3807 {
3808  //Calculate the centroid of points
3809  double cx = 0, cy = 0;
3810  double area, sum = 0;
3811  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
3812  {
3813  const QPointF& p1 = points[i];
3814  const QPointF& p2 = points[j];
3815  area = p1.x() * p2.y() - p1.y() * p2.x();
3816  sum += area;
3817  cx += ( p1.x() + p2.x() ) * area;
3818  cy += ( p1.y() + p2.y() ) * area;
3819  }
3820  sum *= 3.0;
3821  if ( qgsDoubleNear( sum, 0.0 ) )
3822  {
3823  // the linear ring is invalid - let's fall back to a solution that will still
3824  // allow us render at least something (instead of just returning point nan,nan)
3825  if ( points.count() >= 2 )
3826  return QPointF(( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
3827  else if ( points.count() == 1 )
3828  return points[0];
3829  else
3830  return QPointF(); // hopefully we shouldn't ever get here
3831  }
3832  cx /= sum;
3833  cy /= sum;
3834 
3835  return QPointF( cx, cy );
3836 }
3837 
3839 {
3840  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3841 
3842  // check if centroid inside in polygon
3843  if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3844  {
3845  unsigned int i, pointCount = points.count();
3846 
3847  QgsPolyline polyline( pointCount );
3848  for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3849 
3850  QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3851  if ( geom )
3852  {
3853  QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3854 
3855  if ( pointOnSurfaceGeom )
3856  {
3857  QgsPoint point = pointOnSurfaceGeom->asPoint();
3858  delete pointOnSurfaceGeom;
3859  delete geom;
3860 
3861  return QPointF( point.x(), point.y() );
3862  }
3863  delete geom;
3864  }
3865  }
3866  return centroid;
3867 }
3868 
3870 {
3871  bool inside = false;
3872 
3873  double x = point.x();
3874  double y = point.y();
3875 
3876  for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3877  {
3878  const QPointF& p1 = points[i];
3879  const QPointF& p2 = points[j];
3880 
3881  if ( qgsDoubleNear( p1.x(), x ) && qgsDoubleNear( p1.y(), y ) )
3882  return true;
3883 
3884  if (( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3885  {
3886  if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3887  inside = !inside;
3888  }
3889 
3890  j = i;
3891  }
3892  return inside;
3893 }
3894 
3896 {
3897  if ( fieldOrExpression.isEmpty() )
3898  return nullptr;
3899 
3900  QgsExpression* expr = new QgsExpression( fieldOrExpression );
3901  if ( !expr->hasParserError() )
3902  return expr;
3903 
3904  // now try with quoted field name
3905  delete expr;
3906  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
3907  Q_ASSERT( !expr2->hasParserError() );
3908  return expr2;
3909 }
3910 
3912 {
3913  const QgsExpression::Node* n = expression->rootNode();
3914 
3915  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
3916  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
3917 
3918  return expression->expression();
3919 }
3920 
3921 QList<double> QgsSymbolLayerV2Utils::prettyBreaks( double minimum, double maximum, int classes )
3922 {
3923  // C++ implementation of R's pretty algorithm
3924  // Based on code for determining optimal tick placement for statistical graphics
3925  // from the R statistical programming language.
3926  // Code ported from R implementation from 'labeling' R package
3927  //
3928  // Computes a sequence of about 'classes' equally spaced round values
3929  // which cover the range of values from 'minimum' to 'maximum'.
3930  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
3931 
3932  QList<double> breaks;
3933  if ( classes < 1 )
3934  {
3935  breaks.append( maximum );
3936  return breaks;
3937  }
3938 
3939  int minimumCount = static_cast< int >( classes ) / 3;
3940  double shrink = 0.75;
3941  double highBias = 1.5;
3942  double adjustBias = 0.5 + 1.5 * highBias;
3943  int divisions = classes;
3944  double h = highBias;
3945  double cell;
3946  int U;
3947  bool small = false;
3948  double dx = maximum - minimum;
3949 
3950  if ( qgsDoubleNear( dx, 0.0 ) && qgsDoubleNear( maximum, 0.0 ) )
3951  {
3952  cell = 1.0;
3953  small = true;
3954  U = 1;
3955  }
3956  else
3957  {
3958  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
3959  if ( adjustBias >= 1.5 * h + 0.5 )
3960  {
3961  U = 1 + ( 1.0 / ( 1 + h ) );
3962  }
3963  else
3964  {
3965  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
3966  }
3967  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
3968  }
3969 
3970  if ( small )
3971  {
3972  if ( cell > 10 )
3973  {
3974  cell = 9 + cell / 10;
3975  cell = cell * shrink;
3976  }
3977  if ( minimumCount > 1 )
3978  {
3979  cell = cell / minimumCount;
3980  }
3981  }
3982  else
3983  {
3984  cell = dx;
3985  if ( divisions > 1 )
3986  {
3987  cell = cell / divisions;
3988  }
3989  }
3990  if ( cell < 20 * 1e-07 )
3991  {
3992  cell = 20 * 1e-07;
3993  }
3994 
3995  double base = pow( 10.0, floor( log10( cell ) ) );
3996  double unit = base;
3997  if (( 2 * base ) - cell < h *( cell - unit ) )
3998  {
3999  unit = 2.0 * base;
4000  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
4001  {
4002  unit = 5.0 * base;
4003  if (( 10.0 * base ) - cell < h *( cell - unit ) )
4004  {
4005  unit = 10.0 * base;
4006  }
4007  }
4008  }
4009  // Maybe used to correct for the epsilon here??
4010  int start = floor( minimum / unit + 1e-07 );
4011  int end = ceil( maximum / unit - 1e-07 );
4012 
4013  // Extend the range out beyond the data. Does this ever happen??
4014  while ( start * unit > minimum + ( 1e-07 * unit ) )
4015  {
4016  start = start - 1;
4017  }
4018  while ( end * unit < maximum - ( 1e-07 * unit ) )
4019  {
4020  end = end + 1;
4021  }
4022  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
4023 
4024  // If we don't have quite enough labels, extend the range out
4025  // to make more (these labels are beyond the data :( )
4026  int k = floor( 0.5 + end - start );
4027  if ( k < minimumCount )
4028  {
4029  k = minimumCount - k;
4030  if ( start >= 0 )
4031  {
4032  end = end + k / 2;
4033  start = start - k / 2 + k % 2;
4034  }
4035  else
4036  {
4037  start = start - k / 2;
4038  end = end + k / 2 + k % 2;
4039  }
4040  }
4041  double minimumBreak = start * unit;
4042  //double maximumBreak = end * unit;
4043  int count = end - start;
4044 
4045  breaks.reserve( count );
4046  for ( int i = 1; i < count + 1; i++ )
4047  {
4048  breaks.append( minimumBreak + i * unit );
4049  }
4050 
4051  if ( breaks.isEmpty() )
4052  return breaks;
4053 
4054  if ( breaks.first() < minimum )
4055  {
4056  breaks[0] = minimum;
4057  }
4058  if ( breaks.last() > maximum )
4059  {
4060  breaks[breaks.count()-1] = maximum;
4061  }
4062 
4063  return breaks;
4064 }
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:352
static QString encodeSldLineJoinStyle(Qt::PenJoinStyle style)
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
Class for parsing and evaluation of expressions (formerly called "search strings").
qlonglong toLongLong(bool *ok) const
void setForceVectorOutput(bool force)
static QgsSymbolV2::OutputUnit decodeSldUom(const QString &str, double *scaleFactor)
void clear()
void setLocked(bool locked)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
QDomNodeList elementsByTagName(const QString &tagname) const
QImage convertToFormat(Format format, QFlags< Qt::ImageConversionFlag > flags) const
static QgsSymbolV2Map loadSymbols(QDomElement &element)
virtual NodeType nodeType() const =0
Abstract virtual that returns the type of this node.
void setClipFeaturesToExtent(bool clipFeaturesToExtent)
Sets whether features drawn by the symbol should be clipped to the render context&#39;s extent...
Definition: qgssymbolv2.h:215
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
QString cap(int nth) const
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
QString & append(QChar ch)
static void multiplyImageOpacity(QImage *image, qreal alpha)
Multiplies opacity of image pixel values with a (global) transparency value.
QByteArray data(const QString &mimeType) const
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:62
static void drawStippledBackground(QPainter *painter, QRect rect)
static Qt::BrushStyle decodeBrushStyle(const QString &str)
int width() const
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=nullptr, Qt::PenCapStyle *penCapStyle=nullptr, QVector< qreal > *customDashPattern=nullptr, double *dashOffset=nullptr)
static QIcon colorRampPreviewIcon(QgsVectorColorRampV2 *ramp, QSize size)
virtual QString type() const =0
static QIcon symbolLayerPreviewIcon(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit u, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
static QPixmap colorRampPreviewPixmap(QgsVectorColorRampV2 *ramp, QSize size)
bool end()
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about &#39;classes&#39; equally spaced round values which cover the range of values fr...
bool contains(const Key &key) const
GeometryType
Definition: qgis.h:111
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static Q_DECL_DEPRECATED bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &borderColor, double &borderWidth, double &size)
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
static QMimeData * colorToMimeData(const QColor &color)
Creates mime data from a color.
void fillRect(const QRectF &rectangle, const QBrush &brush)
int localeAwareCompare(const QString &other) const
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
QString readLine(qint64 maxlen)
void append(const T &value)
void fill(const QColor &color)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
SymbolType type() const
Definition: qgssymbolv2.h:104
int right() const
iterator begin()
QString name() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QString attribute(const QString &name, const QString &defValue) const
static double mapUnitScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> map units.
int length() const
QString nodeValue() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool hasFormat(const QString &mimeType) const
int weight() const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
void reserve(int alloc)
static QString encodeColor(const QColor &color)
QDateTime toDateTime() const
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
static QString encodeSldUom(QgsSymbolV2::OutputUnit unit, double *scaleFactor)
QString attributeNS(const QString nsURI, const QString &localName, const QString &defValue) const
QgsMultiPolyline asMultiPolyline() const
Return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
static void externalGraphicToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &mime, const QColor &color, double size=-1)
void setColorData(const QVariant &color)
static QgsStringMap getVendorOptionList(QDomElement &element)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
double computeMapUnitsPerPixel(const QgsRenderContext &c) const
Computes a map units per pixel scaling factor, respecting the minimum and maximum scales set for the ...
QgsPolygon asPolygon() const
Return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list...
const_iterator constEnd() const
The output shall be in pixels.
Definition: qgssymbolv2.h:67
Calculate scale by the diameter.
Definition: qgssymbolv2.h:90
const T & at(int i) const
QTime toTime() const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
int size() const
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
static QVector< qreal > decodeRealVector(const QString &s)
static QColor colorFromMimeData(const QMimeData *data, bool &hasAlpha)
Attempts to parse mime data as a color.
QString simplified() const
static bool functionFromSldElement(QDomElement &element, QString &function)
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
QDomElement nextSiblingElement(const QString &tagName) const
static QString encodeSldFontStyle(QFont::Style style)
T value() const
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=nullptr)
Create ogr feature style string for pen.
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
QgsGeometry * pointOnSurface() const
Returns a point within a geometry.
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAlpha(int alpha)
virtual QgsStringMap properties() const =0
Line symbol.
Definition: qgssymbolv2.h:79
int height() const
static QPointF decodePoint(const QString &str)
static bool convertPolygonSymbolizerToPointMarker(QDomElement &element, QgsSymbolLayerV2List &layerList)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QgsSymbolLayerV2Registry * instance()
return the single instance of this class (instantiate it if not exists)
static QVector< qreal > decodeSldRealVector(const QString &s)
QDomElement documentElement() const
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static Qt::BrushStyle decodeSldBrushStyle(const QString &str)
double scaleFactor() const
QString join(const QString &separator) const
bool hasText() const
bool exists() const
static void createOnlineResourceElement(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format)
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
void drawLine(const QLineF &line)
static QStringList listSvgFilesAt(const QString &directory)
Return a list of svg files at the specified directory.
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
void setRgb(int r, int g, int b, int a)
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:288
void clear()
void chop(int n)
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
bool isValidColor(const QString &name)
QDomNodeList childNodes() const
qulonglong toULongLong(bool *ok) const
static bool needMarkerLine(QDomElement &element)
QChar separator()
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr)
Draw icon of the symbol that occupyies area given by size using the painter.
static bool needPointPatternFill(QDomElement &element)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double maxScale
The maximum scale, or 0.0 if unset.
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:261
static QgsSymbolV2 * loadSymbol(const QDomElement &element)
Attempts to load a symbol from a DOM element.
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> pixel dimensions.
Marker symbol.
Definition: qgssymbolv2.h:78
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
int size() const
void setMapUnitScale(const QgsMapUnitScale &scale)
QgsMultiPolygon asMultiPolygon() const
Return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QDomNode nextSibling() const
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
QDomElement toElement() const
static QString encodePenStyle(Qt::PenStyle style)
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
int indexIn(const QString &str, int offset, CaretMode caretMode) const
bool isEmpty() const
T * data()
static QString symbolPathToName(QString path)
Get symbols&#39;s name from its path.
void clear()
Mixed units in symbol layers.
Definition: qgssymbolv2.h: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
uint toUInt(bool *ok) const
void resize(int size)
bool atEnd() const
QString canonicalPath() const
static bool pointInPolygon(const QPolygonF &points, QPointF point)
Calculate whether a point is within of a QPolygonF.
QString text() const
QString text() const
int toInt(bool *ok) const
QString path() const
static QgsSymbolLayerV2 * createFillLayerFromSld(QDomElement &element)
static QString encodePoint(QPointF point)
bool hasAttribute(const QString &name) const
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
static QPointF offsetPoint(QPointF pt, double angle, double dist)
static QString encodeSldAlpha(int alpha)
static bool needLinePatternFill(QDomElement &element)
static QgsPaintEffectRegistry * instance()
Returns a reference to the singleton instance of the paint effect registry.
The ouput shall be a percentage of another measurement (eg canvas size, feature size) ...
Definition: qgssymbolv2.h:68
int top() const
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:200
int captureCount() const
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
int red() const
void setPen(const QColor &color)
int width() const
void setRenderingPass(int renderingPass)
void setAttribute(const QString &name, const QString &value)
int left() const
static QStringList listSvgFiles()
Return a list of all available svg files.
int toInt(bool *ok, int base) const
bool isEmpty() const
QDomNodeList elementsByTagName(const QString &tagname) const
bool isEmpty() const
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)
double rasterScaleFactor() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
static void saveProperties(QgsStringMap props, QDomDocument &doc, QDomElement &element)
virtual QgsStringMap properties() const =0
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
T & first()
QVector< QgsPolyline > QgsPolygon
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:50
iterator end()
static double estimateMaxSymbolBleed(QgsSymbolV2 *symbol)
Returns the maximum estimated bleed for the symbol.
static bool hasExternalGraphic(QDomElement &element)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString scheme() const
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
QVariant colorData() const
int alpha() const
QgsGeometry * buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
A class to represent a point.
Definition: qgspoint.h:65
iterator begin()
static Qt::PenStyle decodePenStyle(const QString &str)
QString toLocalFile() const
virtual QColor color(double value) const =0
int logicalDpiX() const
QDomText createTextNode(const QString &value)
int green() const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
iterator end()
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)
int renderingPass() const
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual void close()
QgsSymbolLayerV2 * createSymbolLayerFromSld(const QString &name, QDomElement &element) const
create a new instance of symbol layer given symbol layer name and SLD
bool isNull() const
virtual QgsSymbolV2 * subSymbol()
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
SymbolType
Type of the symbol.
Definition: qgssymbolv2.h:76
void setTexture(const QPixmap &pixmap)
int bytesPerLine() const
QgsPolyline asPolyline() const
Return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
QDate toDate() const
int blue() const
const T & at(int i) const
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
const_iterator constBegin() const
Contains information about the context of a rendering operation.
bool isValid() const
void save(QTextStream &str, int indent) const
static QDomElement createSvgParameterElement(QDomDocument &doc, const QString &name, const QString &value)
QDomNode firstChild() const
int width() const
QString mid(int position, int n) const
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static QString encodeBrushStyle(Qt::BrushStyle style)
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
static QColor parseColorWithAlpha(const QString &colorStr, bool &containsAlpha, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes...
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
static QPointF linesIntersection(QPointF p1, double t1, QPointF p2, double t2)
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:87
Struct for storing maximum and minimum scales for measurements in map units.
bool isLocked() const
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
static QList< QColor > parseColorList(const QString &colorStr)
Attempts to parse a string as a list of colors using a variety of common formats, including hex codes...
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
QgsGeometry * offsetCurve(double distance, int segments, int joinStyle, double mitreLimit) const
Returns an offset line at a given distance and side from an input line.
bool isEmpty() const
static QDomElement expressionToOgcFilter(const QgsExpression &exp, QDomDocument &doc, QString *errorMessage=nullptr)
Creates OGC filter XML element.
QStringList entryList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
QString family() const
static QMimeData * colorListToMimeData(const QgsNamedColorList &colorList, const bool allFormats=true)
Creates mime data from a list of named colors.
static bool lineInfo(QPointF p1, QPointF p2, double &angle, double &t)
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void setX(qreal x)
void setY(qreal y)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp: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
QgsSymbolLayerV2 * createSymbolLayer(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
create a new instance of symbol layer given symbol layer name and properties
static void labelTextToSld(QDomDocument &doc, QDomElement &element, const QString &label, const QFont &font, const QColor &color=QColor(), double size=-1)
int bottom() const
qreal & rx()
qreal & ry()
QDomComment createComment(const QString &value)
QStringList split(const QString &sep, const QString &str, bool allowEmptyEntries)
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
static QPicture symbolLayerPreviewPicture(QgsSymbolLayerV2 *layer, QgsSymbolV2::OutputUnit units, QSize size, const QgsMapUnitScale &scale=QgsMapUnitScale())
Draws a symbol layer preview to a QPicture.
static QgsGeometry * fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
QString expression() const
Return the original, unmodified expression string.
void push_back(const T &value)
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
static void clearSymbolMap(QgsSymbolV2Map &symbols)
static QStringList svgPaths()
Returns the pathes to svg directories.
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static Qt::PenCapStyle decodeSldLineCapStyle(const QString &str)
int height() const
double toDouble(bool *ok) const
static QColor decodeColor(const QString &str)
iterator insert(const Key &key, const T &value)
static QgsStringMap getSvgParameterList(QDomElement &element)
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
void setRasterScaleFactor(double factor)
Calculate scale by the area.
Definition: qgssymbolv2.h: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)
static bool onlineResourceFromSldElement(QDomElement &element, QString &path, QString &format)
int size() const
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
uint length() const
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
static QgsStringMap parseProperties(QDomElement &element)
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
virtual void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)=0
const_iterator constBegin() const
static bool hasWellKnownMark(QDomElement &element)
Type type() const
static QPointF polygonPointOnSurface(const QPolygonF &points)
Calculate a point within of a QPolygonF.
static Qt::PenJoinStyle decodeSldLineJoinStyle(const QString &str)
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
static QFont::Style decodeSldFontStyle(const QString &str)
int size() const
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context&#39;s extent...
Definition: qgssymbolv2.h:225
bool begin(QPaintDevice *device)
double minScale
The minimum scale, or 0.0 if unset.
int compare(const QString &other) const
static bool opacityFromSldElement(QDomElement &element, QString &alphaFunc)
QString parserErrorString() const
Returns parser error.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
set layer&#39;s subsymbol. takes ownership of the passed symbol
iterator end()
Format format() const
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g.
static bool saveColorsToGpl(QFile &file, const QString &paletteName, const QgsNamedColorList &colors)
Exports colors to a gpl GIMP palette file.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the layer.
void setOutputUnit(QgsSymbolV2::OutputUnit u)
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:202
iterator begin()
T take(const Key &key)
QChar toChar() const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
static QString encodeSldLineCapStyle(Qt::PenCapStyle style)
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the layer.
static void premultiplyColor(QColor &rgb, int alpha)
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value.
QByteArray toByteArray(int indent) const
bool isValid() const
static void createOpacityElement(QDomDocument &doc, QDomElement &element, const QString &alphaFunc)
bool hasColor() const
static QgsNamedColorList colorListFromMimeData(const QMimeData *data)
Attempts to parse mime data as a list of named colors.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
Style style() const
static bool needEllipseMarker(QDomElement &element)
bool isNull() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &borderColor=QColor(), double borderWidth=-1, double size=-1)