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