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