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