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