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