QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 
855 QgsSymbolV2* QgsSymbolLayerV2Utils::loadSymbol( const QDomElement &element )
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  if ( alpha != 255 && alpha > 0 )
3413  {
3414  // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
3415  // where color values have to be premultiplied by alpha
3416  double alphaFactor = alpha / 255.;
3417  int r = 0, g = 0, b = 0;
3418  rgb.getRgb( &r, &g, &b );
3419 
3420  r *= alphaFactor;
3421  g *= alphaFactor;
3422  b *= alphaFactor;
3423  rgb.setRgb( r, g, b, alpha );
3424  }
3425  else if ( alpha == 0 )
3426  {
3427  rgb.setRgb( 0, 0, 0, 0 );
3428  }
3429 }
3430 
3431 #if 0
3432 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs )
3433 {
3434  switch ( lhs.type() )
3435  {
3436  case QVariant::Int:
3437  return lhs.toInt() < rhs.toInt();
3438  case QVariant::UInt:
3439  return lhs.toUInt() < rhs.toUInt();
3440  case QVariant::LongLong:
3441  return lhs.toLongLong() < rhs.toLongLong();
3442  case QVariant::ULongLong:
3443  return lhs.toULongLong() < rhs.toULongLong();
3444  case QVariant::Double:
3445  return lhs.toDouble() < rhs.toDouble();
3446  case QVariant::Char:
3447  return lhs.toChar() < rhs.toChar();
3448  case QVariant::Date:
3449  return lhs.toDate() < rhs.toDate();
3450  case QVariant::Time:
3451  return lhs.toTime() < rhs.toTime();
3452  case QVariant::DateTime:
3453  return lhs.toDateTime() < rhs.toDateTime();
3454  default:
3455  return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0;
3456  }
3457 }
3458 
3459 static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs )
3460 {
3461  return ! _QVariantLessThan( lhs, rhs );
3462 }
3463 #endif
3464 
3465 void QgsSymbolLayerV2Utils::sortVariantList( QList<QVariant>& list, Qt::SortOrder order )
3466 {
3467  if ( order == Qt::AscendingOrder )
3468  {
3469  //qSort( list.begin(), list.end(), _QVariantLessThan );
3470  qSort( list.begin(), list.end(), qgsVariantLessThan );
3471  }
3472  else // Qt::DescendingOrder
3473  {
3474  //qSort( list.begin(), list.end(), _QVariantGreaterThan );
3475  qSort( list.begin(), list.end(), qgsVariantGreaterThan );
3476  }
3477 }
3478 
3479 QPointF QgsSymbolLayerV2Utils::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance )
3480 {
3481  double dx = directionPoint.x() - startPoint.x();
3482  double dy = directionPoint.y() - startPoint.y();
3483  double length = sqrt( dx * dx + dy * dy );
3484  double scaleFactor = distance / length;
3485  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
3486 }
3487 
3488 
3490 {
3491  // copied from QgsMarkerCatalogue - TODO: unify
3492  QStringList list;
3493  QStringList svgPaths = QgsApplication::svgPaths();
3494 
3495  for ( int i = 0; i < svgPaths.size(); i++ )
3496  {
3497  QDir dir( svgPaths[i] );
3498  foreach ( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3499  {
3500  svgPaths.insert( i + 1, dir.path() + "/" + item );
3501  }
3502 
3503  foreach ( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3504  {
3505  // TODO test if it is correct SVG
3506  list.append( dir.path() + "/" + item );
3507  }
3508  }
3509  return list;
3510 }
3511 
3512 // Stripped down version of listSvgFiles() for specified directory
3513 QStringList QgsSymbolLayerV2Utils::listSvgFilesAt( QString directory )
3514 {
3515  // TODO anything that applies for the listSvgFiles() applies this also
3516 
3517  QStringList list;
3518  QStringList svgPaths;
3519  svgPaths.append( directory );
3520 
3521  for ( int i = 0; i < svgPaths.size(); i++ )
3522  {
3523  QDir dir( svgPaths[i] );
3524  foreach ( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
3525  {
3526  svgPaths.insert( i + 1, dir.path() + "/" + item );
3527  }
3528 
3529  foreach ( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
3530  {
3531  list.append( dir.path() + "/" + item );
3532  }
3533  }
3534  return list;
3535 
3536 }
3537 
3539 {
3540  // copied from QgsSymbol::setNamedPointSymbol - TODO: unify
3541 
3542  // we might have a full path...
3543  if ( QFile( name ).exists() )
3544  return QFileInfo( name ).canonicalFilePath();
3545 
3546  // or it might be an url...
3547  if ( name.contains( "://" ) )
3548  {
3549  QUrl url( name );
3550  if ( url.isValid() && !url.scheme().isEmpty() )
3551  {
3552  if ( url.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
3553  {
3554  // it's a url to a local file
3555  name = url.toLocalFile();
3556  if ( QFile( name ).exists() )
3557  {
3558  return QFileInfo( name ).canonicalFilePath();
3559  }
3560  }
3561  else
3562  {
3563  // it's a url pointing to a online resource
3564  return name;
3565  }
3566  }
3567  }
3568 
3569  // SVG symbol not found - probably a relative path was used
3570 
3571  QStringList svgPaths = QgsApplication::svgPaths();
3572  for ( int i = 0; i < svgPaths.size(); i++ )
3573  {
3574  QString svgPath = svgPaths[i];
3575  if ( svgPath.endsWith( QString( "/" ) ) )
3576  {
3577  svgPath.chop( 1 );
3578  }
3579 
3580  QgsDebugMsg( "SvgPath: " + svgPath );
3581  // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
3582  //QFileInfo myInfo( name );
3583  //QString myFileName = myInfo.fileName(); // foo.svg
3584  //QString myLowestDir = myInfo.dir().dirName();
3585  //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : "/" + myLowestDir ) + "/" + myFileName;
3586  QString myLocalPath = svgPath + QDir::separator() + name;
3587 
3588  QgsDebugMsg( "Alternative svg path: " + myLocalPath );
3589  if ( QFile( myLocalPath ).exists() )
3590  {
3591  QgsDebugMsg( "Svg found in alternative path" );
3592  return QFileInfo( myLocalPath ).canonicalFilePath();
3593  }
3594  }
3595 
3596  QFileInfo pfi( QgsProject::instance()->fileName() );
3597  QString alternatePath = pfi.canonicalPath() + QDir::separator() + name;
3598  if ( pfi.exists() && QFile( alternatePath ).exists() )
3599  {
3600  QgsDebugMsg( "Svg found in alternative path" );
3601  return QFileInfo( alternatePath ).canonicalFilePath();
3602  }
3603  else
3604  {
3605  QgsDebugMsg( "Svg not found in project path" );
3606  }
3607  //couldnt find the file, no happy ending :-(
3608  QgsDebugMsg( "Computed alternate path but no svg there either" );
3609 
3610  return QString();
3611 }
3612 
3614 {
3615  // copied from QgsSymbol::writeXML
3616 
3617  QFileInfo fi( path );
3618  if ( !fi.exists() )
3619  return path;
3620 
3621  path = fi.canonicalFilePath();
3622 
3623  QStringList svgPaths = QgsApplication::svgPaths();
3624 
3625  bool isInSvgPathes = false;
3626  for ( int i = 0; i < svgPaths.size(); i++ )
3627  {
3628  QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
3629 
3630  if ( !dir.isEmpty() && path.startsWith( dir ) )
3631  {
3632  path = path.mid( dir.size() + 1 );
3633  isInSvgPathes = true;
3634  break;
3635  }
3636  }
3637 
3638  if ( isInSvgPathes )
3639  return path;
3640 
3641  return QgsProject::instance()->writePath( path );
3642 }
3643 
3644 QPointF QgsSymbolLayerV2Utils::polygonCentroid( const QPolygonF& points )
3645 {
3646  //Calculate the centroid of points
3647  double cx = 0, cy = 0;
3648  double area, sum = 0;
3649  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
3650  {
3651  const QPointF& p1 = points[i];
3652  const QPointF& p2 = points[j];
3653  area = p1.x() * p2.y() - p1.y() * p2.x();
3654  sum += area;
3655  cx += ( p1.x() + p2.x() ) * area;
3656  cy += ( p1.y() + p2.y() ) * area;
3657  }
3658  sum *= 3.0;
3659  if ( sum == 0 )
3660  {
3661  // the linear ring is invalid - let's fall back to a solution that will still
3662  // allow us render at least something (instead of just returning point nan,nan)
3663  if ( points.count() >= 2 )
3664  return QPointF(( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
3665  else if ( points.count() == 1 )
3666  return points[0];
3667  else
3668  return QPointF(); // hopefully we shouldn't ever get here
3669  }
3670  cx /= sum;
3671  cy /= sum;
3672 
3673  return QPointF( cx, cy );
3674 }
3675 
3676 QPointF QgsSymbolLayerV2Utils::polygonPointOnSurface( const QPolygonF& points )
3677 {
3678  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3679 
3680  // check if centroid inside in polygon
3681  if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3682  {
3683  unsigned int i, pointCount = points.count();
3684 
3685  QgsPolyline polyline( pointCount );
3686  for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3687 
3688  QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3689  if ( geom )
3690  {
3691  QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3692 
3693  if ( pointOnSurfaceGeom )
3694  {
3695  QgsPoint point = pointOnSurfaceGeom->asPoint();
3696  delete pointOnSurfaceGeom;
3697  delete geom;
3698 
3699  return QPointF( point.x(), point.y() );
3700  }
3701  delete geom;
3702  }
3703  }
3704  return centroid;
3705 }
3706 
3707 bool QgsSymbolLayerV2Utils::pointInPolygon( const QPolygonF &points, const QPointF &point )
3708 {
3709  bool inside = false;
3710 
3711  double x = point.x();
3712  double y = point.y();
3713 
3714  for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3715  {
3716  const QPointF& p1 = points[i];
3717  const QPointF& p2 = points[j];
3718 
3719  if ( p1.x() == x && p1.y() == y )
3720  return true;
3721 
3722  if (( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3723  {
3724  if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3725  inside = !inside;
3726  }
3727 
3728  j = i;
3729  }
3730  return inside;
3731 }
3732 
3734 {
3735  if ( fieldOrExpression.isEmpty() )
3736  return 0;
3737 
3738  QgsExpression* expr = new QgsExpression( fieldOrExpression );
3739  if ( !expr->hasParserError() )
3740  return expr;
3741 
3742  // now try with quoted field name
3743  delete expr;
3744  QgsExpression* expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
3745  Q_ASSERT( !expr2->hasParserError() );
3746  return expr2;
3747 }
3748 
3750 {
3751  const QgsExpression::Node* n = expression->rootNode();
3752 
3753  if ( n && n->nodeType() == QgsExpression::ntColumnRef )
3754  return static_cast<const QgsExpression::NodeColumnRef*>( n )->name();
3755 
3756  return expression->expression();
3757 }
3758