QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsfillsymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfillsymbollayerv2.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 #include "qgsfillsymbollayerv2.h"
16 #include "qgslinesymbollayerv2.h"
17 #include "qgsmarkersymbollayerv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsdxfexport.h"
20 #include "qgsexpression.h"
21 #include "qgsrendercontext.h"
22 #include "qgsproject.h"
23 #include "qgssvgcache.h"
24 #include "qgslogger.h"
25 #include "qgsvectorcolorrampv2.h"
26 
27 #include <QPainter>
28 #include <QFile>
29 #include <QSvgRenderer>
30 #include <QDomDocument>
31 #include <QDomElement>
32 
33 QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( QColor color, Qt::BrushStyle style, QColor borderColor, Qt::PenStyle borderStyle, double borderWidth )
34  : mBrushStyle( style ), mBorderColor( borderColor ), mBorderStyle( borderStyle ), mBorderWidth( borderWidth ), mBorderWidthUnit( QgsSymbolV2::MM ),
35  mOffsetUnit( QgsSymbolV2::MM )
36 {
37  mColor = color;
38 }
39 
41 {
42  mBorderWidthUnit = unit;
43  mOffsetUnit = unit;
44 }
45 
47 {
49  if ( mOffsetUnit != unit )
50  {
51  return QgsSymbolV2::Mixed;
52  }
53  return unit;
54 }
55 
56 void QgsSimpleFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QBrush& brush, QPen& pen, QPen& selPen )
57 {
58  QgsExpression* colorExpression = expression( "color" );
59  if ( colorExpression )
60  {
61  brush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
62  }
63  QgsExpression* colorBorderExpression = expression( "color_border" );
64  if ( colorBorderExpression )
65  {
66  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
67  }
68  QgsExpression* widthBorderExpression = expression( "width_border" );
69  if ( widthBorderExpression )
70  {
71  double width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
73  pen.setWidthF( width );
74  selPen.setWidthF( width );
75  }
76 }
77 
78 
80 {
82  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
86  QPointF offset;
87 
88  if ( props.contains( "color" ) )
89  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
90  if ( props.contains( "style" ) )
91  style = QgsSymbolLayerV2Utils::decodeBrushStyle( props["style"] );
92  if ( props.contains( "color_border" ) )
93  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
94  if ( props.contains( "style_border" ) )
95  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["style_border"] );
96  if ( props.contains( "width_border" ) )
97  borderWidth = props["width_border"].toDouble();
98  if ( props.contains( "offset" ) )
99  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
100 
101  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, style, borderColor, borderStyle, borderWidth );
102  sl->setOffset( offset );
103  if ( props.contains( "border_width_unit" ) )
104  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["border_width_unit"] ) );
105  if ( props.contains( "offset_unit" ) )
106  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
107 
108  if ( props.contains( "color_expression" ) )
109  {
110  sl->setDataDefinedProperty( "color", props["color_expression"] );
111  }
112  if ( props.contains( "color_border_expression" ) )
113  {
114  sl->setDataDefinedProperty( "color_border", props["color_border_expression"] );
115  }
116  if ( props.contains( "width_border_expression" ) )
117  {
118  sl->setDataDefinedProperty( "width_border", props["width_border_expression"] );
119  }
120  return sl;
121 }
122 
123 
125 {
126  return "SimpleFill";
127 }
128 
130 {
131  QColor fillColor = mColor;
132  fillColor.setAlphaF( context.alpha() * mColor.alphaF() );
133  mBrush = QBrush( fillColor, mBrushStyle );
134 
135  // scale brush content for printout
137  if ( rasterScaleFactor != 1.0 )
138  {
139  mBrush.setMatrix( QMatrix().scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ) );
140  }
141 
142  QColor selColor = context.renderContext().selectionColor();
143  QColor selPenColor = selColor == mColor ? selColor : mBorderColor;
144  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
145  mSelBrush = QBrush( selColor );
146  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
147  // this would mean symbols with "no fill" look the same whether or not they are selected
148  if ( selectFillStyle )
149  mSelBrush.setStyle( mBrushStyle );
150 
151  QColor borderColor = mBorderColor;
152  borderColor.setAlphaF( context.alpha() * mBorderColor.alphaF() );
153  mPen = QPen( borderColor );
154  mSelPen = QPen( selPenColor );
155  mPen.setStyle( mBorderStyle );
157  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
158 }
159 
161 {
162  Q_UNUSED( context );
163 }
164 
165 void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
166 {
167  QPainter* p = context.renderContext().painter();
168  if ( !p )
169  {
170  return;
171  }
172 
174 
175  p->setBrush( context.selected() ? mSelBrush : mBrush );
176  p->setPen( context.selected() ? mSelPen : mPen );
177 
178  QPointF offset;
179  if ( !mOffset.isNull() )
180  {
183  p->translate( offset );
184  }
185 
186  _renderPolygon( p, points, rings, context );
187 
188  if ( !mOffset.isNull() )
189  {
190  p->translate( -offset );
191  }
192 }
193 
195 {
196  QgsStringMap map;
197  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
199  map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
200  map["style_border"] = QgsSymbolLayerV2Utils::encodePenStyle( mBorderStyle );
201  map["width_border"] = QString::number( mBorderWidth );
202  map["border_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mBorderWidthUnit );
203  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
206  return map;
207 }
208 
210 {
212  sl->setOffset( mOffset );
213  sl->setOffsetUnit( mOffsetUnit );
216  return sl;
217 }
218 
219 void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
220 {
221  if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
222  return;
223 
224  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
225  if ( !props.value( "uom", "" ).isEmpty() )
226  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
227  element.appendChild( symbolizerElem );
228 
229  // <Geometry>
230  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
231 
232  if ( mBrushStyle != Qt::NoBrush )
233  {
234  // <Fill>
235  QDomElement fillElem = doc.createElement( "se:Fill" );
236  symbolizerElem.appendChild( fillElem );
238  }
239 
240  if ( mBorderStyle != Qt::NoPen )
241  {
242  // <Stroke>
243  QDomElement strokeElem = doc.createElement( "se:Stroke" );
244  symbolizerElem.appendChild( strokeElem );
246  }
247 
248  // <se:Displacement>
250 }
251 
252 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
253 {
254  //brush
255  QString symbolStyle;
256  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStyleBrush( mColor ) );
257  symbolStyle.append( ";" );
258  //pen
259  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor ) );
260  return symbolStyle;
261 }
262 
264 {
265  QgsDebugMsg( "Entered." );
266 
267  QColor color, borderColor;
268  Qt::BrushStyle fillStyle;
269  Qt::PenStyle borderStyle;
270  double borderWidth;
271 
272  QDomElement fillElem = element.firstChildElement( "Fill" );
273  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
274 
275  QDomElement strokeElem = element.firstChildElement( "Stroke" );
276  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
277 
278  QPointF offset;
280 
281  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
282  sl->setOffset( offset );
283  return sl;
284 }
285 
287 {
288  double penBleed = mBorderStyle == Qt::NoPen ? 0 : ( mBorderWidth / 2.0 );
289  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
290  return penBleed + offsetBleed;
291 }
292 
293 double QgsSimpleFillSymbolLayerV2::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
294 {
295  double width = mBorderWidth;
296  QgsExpression* widthBorderExpression = expression( "width_border" );
297  if ( widthBorderExpression )
298  {
299  width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
300  }
301  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), mBorderWidthUnit, e.mapUnits() );
302 }
303 
305 {
306  if ( mBrushStyle == Qt::NoBrush )
307  {
308  QgsExpression* colorBorderExpression = expression( "color_border" );
309  if ( colorBorderExpression )
310  {
311  return QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
312  }
313  return mBorderColor;
314  }
315  else
316  {
317  QgsExpression* colorExpression = expression( "color" );
318  if ( colorExpression )
319  {
320  return QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
321  }
322  return mColor;
323  }
324 }
325 
327 {
328  return mBorderStyle;
329 }
330 
331 //QgsGradientFillSymbolLayer
332 
334  GradientColorType colorType, GradientType gradientType,
335  GradientCoordinateMode coordinateMode, GradientSpread spread )
336  : mGradientColorType( colorType ),
337  mGradientRamp( NULL ),
338  mGradientType( gradientType ),
339  mCoordinateMode( coordinateMode ),
340  mGradientSpread( spread ),
341  mReferencePoint1( QPointF( 0.5, 0 ) ),
342  mReferencePoint1IsCentroid( false ),
343  mReferencePoint2( QPointF( 0.5, 1 ) ),
344  mReferencePoint2IsCentroid( false ),
345  mAngle( 0 ),
346  mOffsetUnit( QgsSymbolV2::MM )
347 {
348  mColor = color;
349  mColor2 = color2;
350 }
351 
353 {
354  delete mGradientRamp;
355 }
356 
358 {
359  //default to a two-color, linear gradient with feature mode and pad spreading
364  //default to gradient from the default fill color to white
365  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
366  QPointF referencePoint1 = QPointF( 0.5, 0 );
367  bool refPoint1IsCentroid = false;
368  QPointF referencePoint2 = QPointF( 0.5, 1 );
369  bool refPoint2IsCentroid = false;
370  double angle = 0;
371  QPointF offset;
372 
373  //update gradient properties from props
374  if ( props.contains( "type" ) )
375  type = ( GradientType )props["type"].toInt();
376  if ( props.contains( "coordinate_mode" ) )
377  coordinateMode = ( GradientCoordinateMode )props["coordinate_mode"].toInt();
378  if ( props.contains( "spread" ) )
379  gradientSpread = ( GradientSpread )props["spread"].toInt();
380  if ( props.contains( "color_type" ) )
381  colorType = ( GradientColorType )props["color_type"].toInt();
382  if ( props.contains( "gradient_color" ) )
383  color = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color"] );
384  if ( props.contains( "gradient_color2" ) )
385  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
386  if ( props.contains( "reference_point1" ) )
387  referencePoint1 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point1"] );
388  if ( props.contains( "reference_point1_iscentroid" ) )
389  refPoint1IsCentroid = props["reference_point1_iscentroid"].toInt();
390  if ( props.contains( "reference_point2" ) )
391  referencePoint2 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point2"] );
392  if ( props.contains( "reference_point2_iscentroid" ) )
393  refPoint2IsCentroid = props["reference_point2_iscentroid"].toInt();
394  if ( props.contains( "angle" ) )
395  angle = props["angle"].toDouble();
396  if ( props.contains( "offset" ) )
397  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
398 
399  //attempt to create color ramp from props
401 
402  //create a new gradient fill layer with desired properties
403  QgsGradientFillSymbolLayerV2* sl = new QgsGradientFillSymbolLayerV2( color, color2, colorType, type, coordinateMode, gradientSpread );
404  sl->setOffset( offset );
405  if ( props.contains( "offset_unit" ) )
406  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
407  sl->setReferencePoint1( referencePoint1 );
408  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
409  sl->setReferencePoint2( referencePoint2 );
410  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
411  sl->setAngle( angle );
412  if ( gradientRamp )
413  sl->setColorRamp( gradientRamp );
414 
415  //data defined symbology expressions
416  if ( props.contains( "color_expression" ) )
417  sl->setDataDefinedProperty( "color", props["color_expression"] );
418  if ( props.contains( "color2_expression" ) )
419  sl->setDataDefinedProperty( "color2", props["color2_expression"] );
420  if ( props.contains( "angle_expression" ) )
421  sl->setDataDefinedProperty( "angle", props["angle_expression"] );
422  if ( props.contains( "gradient_type_expression" ) )
423  sl->setDataDefinedProperty( "gradient_type", props["gradient_type_expression"] );
424  if ( props.contains( "coordinate_mode_expression" ) )
425  sl->setDataDefinedProperty( "coordinate_mode", props["coordinate_mode_expression"] );
426  if ( props.contains( "spread_expression" ) )
427  sl->setDataDefinedProperty( "spread", props["spread_expression"] );
428  if ( props.contains( "reference1_x_expression" ) )
429  sl->setDataDefinedProperty( "reference1_x", props["reference1_x_expression"] );
430  if ( props.contains( "reference1_y_expression" ) )
431  sl->setDataDefinedProperty( "reference1_y", props["reference1_y_expression"] );
432  if ( props.contains( "reference1_iscentroid_expression" ) )
433  sl->setDataDefinedProperty( "reference1_iscentroid", props["reference1_iscentroid_expression"] );
434  if ( props.contains( "reference2_x_expression" ) )
435  sl->setDataDefinedProperty( "reference2_x", props["reference2_x_expression"] );
436  if ( props.contains( "reference2_y_expression" ) )
437  sl->setDataDefinedProperty( "reference2_y", props["reference2_y_expression"] );
438  if ( props.contains( "reference2_iscentroid_expression" ) )
439  sl->setDataDefinedProperty( "reference2_iscentroid", props["reference2_iscentroid_expression"] );
440 
441  return sl;
442 }
443 
445 {
446  delete mGradientRamp;
447  mGradientRamp = ramp;
448 }
449 
451 {
452  return "GradientFill";
453 }
454 
456 {
457  //first gradient color
458  QgsExpression* colorExpression = expression( "color" );
459  QColor color = mColor;
460  if ( colorExpression )
461  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
462 
463  //second gradient color
464  QgsExpression* colorExpression2 = expression( "color2" );
465  QColor color2 = mColor2;
466  if ( colorExpression2 )
467  color2 = QgsSymbolLayerV2Utils::decodeColor( colorExpression2->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
468 
469  //gradient rotation angle
470  QgsExpression* angleExpression = expression( "angle" );
471  double angle = mAngle;
472  if ( angleExpression )
473  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
474 
475  //gradient type
476  QgsExpression* typeExpression = expression( "gradient_type" );
478  if ( typeExpression )
479  {
480  QString currentType = typeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
481  if ( currentType == QObject::tr( "linear" ) )
482  {
484  }
485  else if ( currentType == QObject::tr( "radial" ) )
486  {
488  }
489  else if ( currentType == QObject::tr( "conical" ) )
490  {
492  }
493  else
494  {
495  //default to linear
497  }
498  }
499 
500  //coordinate mode
501  QgsExpression* coordModeExpression = expression( "coordinate_mode" );
503  if ( coordModeExpression )
504  {
505  QString currentCoordMode = coordModeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
506  if ( currentCoordMode == QObject::tr( "feature" ) )
507  {
508  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
509  }
510  else if ( currentCoordMode == QObject::tr( "viewport" ) )
511  {
513  }
514  else
515  {
516  //default to feature mode
517  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
518  }
519  }
520 
521  //gradient spread
522  QgsExpression* spreadExpression = expression( "spread" );
524  if ( spreadExpression )
525  {
526  QString currentSpread = spreadExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
527  if ( currentSpread == QObject::tr( "pad" ) )
528  {
530  }
531  else if ( currentSpread == QObject::tr( "repeat" ) )
532  {
534  }
535  else if ( currentSpread == QObject::tr( "reflect" ) )
536  {
538  }
539  else
540  {
541  //default to pad spread
543  }
544  }
545 
546  //reference point 1 x & y
547  QgsExpression* ref1XExpression = expression( "reference1_x" );
548  double refPoint1X = mReferencePoint1.x();
549  if ( ref1XExpression )
550  refPoint1X = ref1XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
551  QgsExpression* ref1YExpression = expression( "reference1_y" );
552  double refPoint1Y = mReferencePoint1.y();
553  if ( ref1YExpression )
554  refPoint1Y = ref1YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
555  QgsExpression* ref1IsCentroidExpression = expression( "reference1_iscentroid" );
556  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
557  if ( ref1IsCentroidExpression )
558  refPoint1IsCentroid = ref1IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
559 
560  //reference point 2 x & y
561  QgsExpression* ref2XExpression = expression( "reference2_x" );
562  double refPoint2X = mReferencePoint2.x();
563  if ( ref2XExpression )
564  refPoint2X = ref2XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
565  QgsExpression* ref2YExpression = expression( "reference2_y" );
566  double refPoint2Y = mReferencePoint2.y();
567  if ( ref2YExpression )
568  refPoint2Y = ref2YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
569  QgsExpression* ref2IsCentroidExpression = expression( "reference2_iscentroid" );
570  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
571  if ( ref2IsCentroidExpression )
572  refPoint2IsCentroid = ref2IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
573 
574  if ( refPoint1IsCentroid || refPoint2IsCentroid )
575  {
576  //either the gradient is starting or ending at a centroid, so calculate it
577  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
578  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
579  QRectF bbox = points.boundingRect();
580  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
581  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
582 
583  if ( refPoint1IsCentroid )
584  {
585  refPoint1X = centroidX;
586  refPoint1Y = centroidY;
587  }
588  if ( refPoint2IsCentroid )
589  {
590  refPoint2X = centroidX;
591  refPoint2Y = centroidY;
592  }
593  }
594 
595  //update gradient with data defined values
596  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
597  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
598 }
599 
600 QPointF QgsGradientFillSymbolLayerV2::rotateReferencePoint( const QPointF & refPoint, double angle )
601 {
602  //rotate a reference point by a specified angle around the point (0.5, 0.5)
603 
604  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
605  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
606  //rotate this line by the current rotation angle
607  refLine.setAngle( refLine.angle() + angle );
608  //get new end point of line
609  QPointF rotatedReferencePoint = refLine.p2();
610  //make sure coords of new end point is within [0, 1]
611  if ( rotatedReferencePoint.x() > 1 )
612  rotatedReferencePoint.setX( 1 );
613  if ( rotatedReferencePoint.x() < 0 )
614  rotatedReferencePoint.setX( 0 );
615  if ( rotatedReferencePoint.y() > 1 )
616  rotatedReferencePoint.setY( 1 );
617  if ( rotatedReferencePoint.y() < 0 )
618  rotatedReferencePoint.setY( 0 );
619 
620  return rotatedReferencePoint;
621 }
622 
624  const QColor &color, const QColor &color2, const GradientColorType &gradientColorType,
625  QgsVectorColorRampV2 *gradientRamp, const GradientType &gradientType,
626  const GradientCoordinateMode &coordinateMode, const GradientSpread &gradientSpread,
627  const QPointF &referencePoint1, const QPointF &referencePoint2, const double angle )
628 {
629  //update alpha of gradient colors
630  QColor fillColor = color;
631  fillColor.setAlphaF( context.alpha() * fillColor.alphaF() );
632  QColor fillColor2 = color2;
633  fillColor2.setAlphaF( context.alpha() * fillColor2.alphaF() );
634 
635  //rotate reference points
636  QPointF rotatedReferencePoint1 = angle != 0 ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
637  QPointF rotatedReferencePoint2 = angle != 0 ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
638 
639  //create a QGradient with the desired properties
640  QGradient gradient;
641  switch ( gradientType )
642  {
644  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
645  break;
647  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
648  break;
650  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
651  break;
652  }
653  switch ( coordinateMode )
654  {
656  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
657  break;
659  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
660  break;
661  }
662  switch ( gradientSpread )
663  {
665  gradient.setSpread( QGradient::PadSpread );
666  break;
668  gradient.setSpread( QGradient::ReflectSpread );
669  break;
671  gradient.setSpread( QGradient::RepeatSpread );
672  break;
673  }
674 
675  //add stops to gradient
676  if ( gradientColorType == QgsGradientFillSymbolLayerV2::ColorRamp && gradientRamp && gradientRamp->type() == "gradient" )
677  {
678  //color ramp gradient
679  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( gradientRamp );
680  gradRamp->addStopsToGradient( &gradient );
681  }
682  else
683  {
684  //two color gradient
685  gradient.setColorAt( 0.0, fillColor );
686  gradient.setColorAt( 1.0, fillColor2 );
687  }
688 
689  //update QBrush use gradient
690  brush = QBrush( gradient );
691 }
692 
694 {
695  QColor selColor = context.renderContext().selectionColor();
696  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
697  mSelBrush = QBrush( selColor );
698 
699  //update mBrush to use a gradient fill with specified properties
700  prepareExpressions( context.layer() );
701 }
702 
704 {
705  Q_UNUSED( context );
706 }
707 
708 void QgsGradientFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
709 {
710  QPainter* p = context.renderContext().painter();
711  if ( !p )
712  {
713  return;
714  }
715 
716  QPen mSelPen;
717  applyDataDefinedSymbology( context, points );
718 
719  p->setBrush( context.selected() ? mSelBrush : mBrush );
720  p->setPen( QPen( Qt::NoPen ) );
721 
722  QPointF offset;
723  if ( !mOffset.isNull() )
724  {
727  p->translate( offset );
728  }
729 
730  _renderPolygon( p, points, rings, context );
731 
732  if ( !mOffset.isNull() )
733  {
734  p->translate( -offset );
735  }
736 }
737 
739 {
740  QgsStringMap map;
741  map["gradient_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
742  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
743  map["color_type"] = QString::number( mGradientColorType );
744  map["type"] = QString::number( mGradientType );
745  map["coordinate_mode"] = QString::number( mCoordinateMode );
746  map["spread"] = QString::number( mGradientSpread );
747  map["reference_point1"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint1 );
748  map["reference_point1_iscentroid"] = QString::number( mReferencePoint1IsCentroid );
749  map["reference_point2"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint2 );
750  map["reference_point2_iscentroid"] = QString::number( mReferencePoint2IsCentroid );
751  map["angle"] = QString::number( mAngle );
752  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
755  if ( mGradientRamp )
756  {
757  map.unite( mGradientRamp->properties() );
758  }
759  return map;
760 }
761 
763 {
765  if ( mGradientRamp )
766  sl->setColorRamp( mGradientRamp->clone() );
771  sl->setAngle( mAngle );
772  sl->setOffset( mOffset );
773  sl->setOffsetUnit( mOffsetUnit );
775  return sl;
776 }
777 
779 {
780  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
781  return offsetBleed;
782 }
783 
784 //QgsImageFillSymbolLayer
785 
786 QgsImageFillSymbolLayer::QgsImageFillSymbolLayer(): mOutlineWidth( 0.0 ), mOutlineWidthUnit( QgsSymbolV2::MM ), mOutline( 0 )
787 {
788  setSubSymbol( new QgsLineSymbolV2() );
789 }
790 
792 {
793 }
794 
795 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
796 {
797  QPainter* p = context.renderContext().painter();
798  if ( !p )
799  {
800  return;
801  }
802 
803  mNextAngle = mAngle;
804  applyDataDefinedSettings( context );
805 
806  p->setPen( QPen( Qt::NoPen ) );
807  if ( context.selected() )
808  {
809  QColor selColor = context.renderContext().selectionColor();
810  // Alister - this doesn't seem to work here
811  //if ( ! selectionIsOpaque )
812  // selColor.setAlphaF( context.alpha() );
813  p->setBrush( QBrush( selColor ) );
814  _renderPolygon( p, points, rings, context );
815  }
816 
817  if ( qgsDoubleNear( mNextAngle, 0.0 ) )
818  {
819  p->setBrush( mBrush );
820  }
821  else
822  {
823  QTransform t = mBrush.transform();
824  t.rotate( mNextAngle );
825  QBrush rotatedBrush = mBrush;
826  rotatedBrush.setTransform( t );
827  p->setBrush( rotatedBrush );
828  }
829  _renderPolygon( p, points, rings, context );
830  if ( mOutline )
831  {
832  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
833  if ( rings )
834  {
835  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
836  for ( ; ringIt != rings->constEnd(); ++ringIt )
837  {
838  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
839  }
840  }
841  }
842 }
843 
845 {
846  if ( !symbol ) //unset current outline
847  {
848  delete mOutline;
849  mOutline = 0;
850  return true;
851  }
852 
853  if ( symbol->type() != QgsSymbolV2::Line )
854  {
855  delete symbol;
856  return false;
857  }
858 
859  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
860  if ( lineSymbol )
861  {
862  delete mOutline;
863  mOutline = lineSymbol;
864  return true;
865  }
866 
867  delete symbol;
868  return false;
869 }
870 
871 
873 {
874  if ( mOutline && mOutline->symbolLayer( 0 ) )
875  {
876  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
877  return subLayerBleed;
878  }
879  return 0;
880 }
881 
882 double QgsImageFillSymbolLayer::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
883 {
884  double width = mOutlineWidth;
885  QgsExpression* widthExpression = expression( "width" );
886  if ( widthExpression )
887  {
888  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
889  }
890  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), mOutlineWidthUnit, e.mapUnits() );
891 }
892 
894 {
895  Q_UNUSED( context );
896  if ( !mOutline )
897  {
898  return QColor( Qt::black );
899  }
900  return mOutline->color();
901 }
902 
904 {
905  return Qt::SolidLine;
906 #if 0
907  if ( !mOutline )
908  {
909  return Qt::SolidLine;
910  }
911  else
912  {
913  return mOutline->dxfPenStyle();
914  }
915 #endif //0
916 }
917 
918 
919 //QgsSVGFillSymbolLayer
920 
921 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
922  mPatternWidthUnit( QgsSymbolV2::MM ), mSvgOutlineWidthUnit( QgsSymbolV2::MM )
923 {
924  setSvgFilePath( svgFilePath );
925  mOutlineWidth = 0.3;
926  mAngle = angle;
928  mSvgPattern = 0;
929 }
930 
931 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
932  mSvgData( svgData )
933 {
934  storeViewBox();
935  mOutlineWidth = 0.3;
936  mAngle = angle;
937  setSubSymbol( new QgsLineSymbolV2() );
939  mSvgPattern = 0;
940 }
941 
943 {
944  delete mSvgPattern;
945 }
946 
948 {
949  mPatternWidthUnit = unit;
950  mSvgOutlineWidthUnit = unit;
951  mOutlineWidthUnit = unit;
952 }
953 
955 {
957  if ( mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
958  {
959  return QgsSymbolV2::Mixed;
960  }
961  return unit;
962 }
963 
964 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
965 {
967  storeViewBox();
968 
969  mSvgFilePath = svgPath;
971 }
972 
974 {
975  QByteArray data;
976  double width = 20;
977  QString svgFilePath;
978  double angle = 0.0;
979 
980  if ( properties.contains( "width" ) )
981  {
982  width = properties["width"].toDouble();
983  }
984  if ( properties.contains( "svgFile" ) )
985  {
986  QString svgName = properties["svgFile"];
987  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
988  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
989  }
990  if ( properties.contains( "angle" ) )
991  {
992  angle = properties["angle"].toDouble();
993  }
994 
995  QgsSVGFillSymbolLayer* symbolLayer = 0;
996  if ( !svgFilePath.isEmpty() )
997  {
998  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
999  }
1000  else
1001  {
1002  if ( properties.contains( "data" ) )
1003  {
1004  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1005  }
1006  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1007  }
1008 
1009  //svg parameters
1010  if ( properties.contains( "svgFillColor" ) )
1011  {
1012  symbolLayer->setSvgFillColor( QColor( properties["svgFillColor"] ) );
1013  }
1014  if ( properties.contains( "svgOutlineColor" ) )
1015  {
1016  symbolLayer->setSvgOutlineColor( QColor( properties["svgOutlineColor"] ) );
1017  }
1018  if ( properties.contains( "svgOutlineWidth" ) )
1019  {
1020  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1021  }
1022 
1023  //units
1024  if ( properties.contains( "pattern_width_unit" ) )
1025  {
1026  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1027  }
1028  if ( properties.contains( "svg_outline_width_unit" ) )
1029  {
1030  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1031  }
1032  if ( properties.contains( "outline_width_unit" ) )
1033  {
1034  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1035  }
1036 
1037  if ( properties.contains( "width_expression" ) )
1038  symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
1039  if ( properties.contains( "svgFile_expression" ) )
1040  symbolLayer->setDataDefinedProperty( "svgFile", properties["svgFile_expression"] );
1041  if ( properties.contains( "angle_expression" ) )
1042  symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
1043  if ( properties.contains( "svgFillColor_expression" ) )
1044  symbolLayer->setDataDefinedProperty( "svgFillColor", "svgFillColor_expression" );
1045  if ( properties.contains( "svgOutlineColor_expression" ) )
1046  symbolLayer->setDataDefinedProperty( "svgOutlineColor", "svgOutlineColor_expression" );
1047  if ( properties.contains( "svgOutlineWidth" ) )
1048  symbolLayer->setDataDefinedProperty( "svgOutlineWidth", "svgOutlineWidth_expression" );
1049 
1050  return symbolLayer;
1051 }
1052 
1054 {
1055  return "SVGFill";
1056 }
1057 
1058 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1059  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1060  QgsSymbolV2::OutputUnit svgOutlineWidthUnit, const QgsSymbolV2RenderContext& context )
1061 {
1062  if ( mSvgViewBox.isNull() )
1063  {
1064  return;
1065  }
1066 
1067  delete mSvgPattern;
1068  mSvgPattern = 0;
1070 
1071  if (( int )size < 1.0 || 10000.0 < size )
1072  {
1073  mSvgPattern = new QImage();
1074  brush.setTextureImage( *mSvgPattern );
1075  }
1076  else
1077  {
1078  bool fitsInCache = true;
1080  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1081  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1082  if ( !fitsInCache )
1083  {
1084  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1085  context.renderContext().scaleFactor(), 1.0 );
1086  double hwRatio = 1.0;
1087  if ( patternPict.width() > 0 )
1088  {
1089  hwRatio = ( double )patternPict.height() / ( double )patternPict.width();
1090  }
1091  mSvgPattern = new QImage(( int )size, ( int )( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1092  mSvgPattern->fill( 0 ); // transparent background
1093 
1094  QPainter p( mSvgPattern );
1095  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1096  }
1097 
1098  QTransform brushTransform;
1099  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1100  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1101  {
1102  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1103  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1104  brush.setTextureImage( transparentImage );
1105  }
1106  else
1107  {
1108  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1109  }
1110  brush.setTransform( brushTransform );
1111  }
1112 }
1113 
1115 {
1116 
1118 
1119  if ( mOutline )
1120  {
1121  mOutline->startRender( context.renderContext() );
1122  }
1123 
1124  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
1125 }
1126 
1128 {
1129  if ( mOutline )
1130  {
1131  mOutline->stopRender( context.renderContext() );
1132  }
1133 }
1134 
1136 {
1137  QgsStringMap map;
1138  if ( !mSvgFilePath.isEmpty() )
1139  {
1140  map.insert( "svgFile", QgsSymbolLayerV2Utils::symbolPathToName( mSvgFilePath ) );
1141  }
1142  else
1143  {
1144  map.insert( "data", QString( mSvgData.toHex() ) );
1145  }
1146 
1147  map.insert( "width", QString::number( mPatternWidth ) );
1148  map.insert( "angle", QString::number( mAngle ) );
1149 
1150  //svg parameters
1151  map.insert( "svgFillColor", mSvgFillColor.name() );
1152  map.insert( "svgOutlineColor", mSvgOutlineColor.name() );
1153  map.insert( "svgOutlineWidth", QString::number( mSvgOutlineWidth ) );
1154 
1155  //units
1156  map["pattern_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit );
1157  map["svg_outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit );
1158  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
1159 
1161  return map;
1162 }
1163 
1165 {
1166  QgsSVGFillSymbolLayer* clonedLayer = 0;
1167  if ( !mSvgFilePath.isEmpty() )
1168  {
1169  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
1170  clonedLayer->setSvgFillColor( mSvgFillColor );
1171  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
1172  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
1173  }
1174  else
1175  {
1176  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
1177  }
1178 
1179  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
1181  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
1182 
1183  if ( mOutline )
1184  {
1185  clonedLayer->setSubSymbol( mOutline->clone() );
1186  }
1187  copyDataDefinedProperties( clonedLayer );
1188  return clonedLayer;
1189 }
1190 
1191 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1192 {
1193  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
1194  if ( !props.value( "uom", "" ).isEmpty() )
1195  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1196  element.appendChild( symbolizerElem );
1197 
1198  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1199 
1200  QDomElement fillElem = doc.createElement( "se:Fill" );
1201  symbolizerElem.appendChild( fillElem );
1202 
1203  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1204  fillElem.appendChild( graphicFillElem );
1205 
1206  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1207  graphicFillElem.appendChild( graphicElem );
1208 
1209  if ( !mSvgFilePath.isEmpty() )
1210  {
1212  }
1213  else
1214  {
1215  // TODO: create svg from data
1216  // <se:InlineContent>
1217  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
1218  }
1219 
1220  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
1221  {
1222  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
1223  }
1224 
1225  // <Rotation>
1226  QString angleFunc;
1227  bool ok;
1228  double angle = props.value( "angle", "0" ).toDouble( &ok );
1229  if ( !ok )
1230  {
1231  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1232  }
1233  else if ( angle + mAngle != 0 )
1234  {
1235  angleFunc = QString::number( angle + mAngle );
1236  }
1237  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1238 
1239  if ( mOutline )
1240  {
1241  // the outline sub symbol should be stored within the Stroke element,
1242  // but it will be stored in a separated LineSymbolizer because it could
1243  // have more than one layer
1244  mOutline->toSld( doc, element, props );
1245  }
1246 }
1247 
1249 {
1250  QgsDebugMsg( "Entered." );
1251 
1252  QString path, mimeType;
1253  QColor fillColor, borderColor;
1254  Qt::PenStyle penStyle;
1255  double size, borderWidth;
1256 
1257  QDomElement fillElem = element.firstChildElement( "Fill" );
1258  if ( fillElem.isNull() )
1259  return NULL;
1260 
1261  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1262  if ( graphicFillElem.isNull() )
1263  return NULL;
1264 
1265  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1266  if ( graphicElem.isNull() )
1267  return NULL;
1268 
1269  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
1270  return NULL;
1271 
1272  if ( mimeType != "image/svg+xml" )
1273  return NULL;
1274 
1275  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
1276 
1277  double angle = 0.0;
1278  QString angleFunc;
1279  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1280  {
1281  bool ok;
1282  double d = angleFunc.toDouble( &ok );
1283  if ( ok )
1284  angle = d;
1285  }
1286 
1287  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
1288  sl->setSvgFillColor( fillColor );
1289  sl->setSvgOutlineColor( borderColor );
1290  sl->setSvgOutlineWidth( borderWidth );
1291 
1292  // try to get the outline
1293  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1294  if ( !strokeElem.isNull() )
1295  {
1297  if ( l )
1298  {
1299  QgsSymbolLayerV2List layers;
1300  layers.append( l );
1301  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
1302  }
1303  }
1304 
1305  return sl;
1306 }
1307 
1309 {
1310  QgsExpression* widthExpression = expression( "width" );
1311  QgsExpression* svgFileExpression = expression( "svgFile" );
1312  QgsExpression* fillColorExpression = expression( "svgFillColor" );
1313  QgsExpression* outlineColorExpression = expression( "svgOutlineColor" );
1314  QgsExpression* outlineWidthExpression = expression( "svgOutlineWidth" );
1315  QgsExpression* angleExpression = expression( "angle" );
1316  if ( !widthExpression && !svgFileExpression && !fillColorExpression && !outlineColorExpression && !outlineWidthExpression && !angleExpression )
1317  {
1318  return; //no data defined settings
1319  }
1320 
1321  if ( angleExpression )
1322  {
1323  mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1324  }
1325 
1326  double width = mPatternWidth;
1327  if ( widthExpression )
1328  {
1329  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1330  }
1331  QString svgFile = mSvgFilePath;
1332  if ( svgFileExpression )
1333  {
1334  svgFile = svgFileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
1335  }
1336  QColor svgFillColor = mSvgFillColor;
1337  if ( fillColorExpression )
1338  {
1339  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1340  }
1342  if ( outlineColorExpression )
1343  {
1344  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1345  }
1346  double outlineWidth = mSvgOutlineWidth;
1347  if ( outlineWidthExpression )
1348  {
1349  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1350  }
1351  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
1352  mSvgOutlineWidthUnit, context );
1353 
1354 }
1355 
1357 {
1358  if ( !mSvgData.isEmpty() )
1359  {
1360  QSvgRenderer r( mSvgData );
1361  if ( r.isValid() )
1362  {
1363  mSvgViewBox = r.viewBoxF();
1364  return;
1365  }
1366  }
1367 
1368  mSvgViewBox = QRectF();
1369  return;
1370 }
1371 
1373 {
1374  //default values
1375  mSvgFillColor = QColor( 0, 0, 0 );
1376  mSvgOutlineColor = QColor( 0, 0, 0 );
1377  mSvgOutlineWidth = 0.3;
1378 
1379  if ( mSvgFilePath.isEmpty() )
1380  {
1381  return;
1382  }
1383 
1384  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
1385  QColor defaultFillColor, defaultOutlineColor;
1386  double defaultOutlineWidth;
1387  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam,
1388  defaultOutlineWidth );
1389 
1390  if ( hasFillParam )
1391  {
1392  mSvgFillColor = defaultFillColor;
1393  }
1394  if ( hasOutlineParam )
1395  {
1396  mSvgOutlineColor = defaultOutlineColor;
1397  }
1398  if ( hasOutlineWidthParam )
1399  {
1400  mSvgOutlineWidth = defaultOutlineWidth;
1401  }
1402 }
1403 
1404 
1406  mOffsetUnit( QgsSymbolV2::MM ), mFillLineSymbol( 0 )
1407 {
1408  setSubSymbol( new QgsLineSymbolV2() );
1409  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
1410 }
1411 
1413 {
1414  mFillLineSymbol->setWidth( w );
1415  mLineWidth = w;
1416 }
1417 
1419 {
1420  mFillLineSymbol->setColor( c );
1421  mColor = c;
1422 }
1423 
1425 {
1426  delete mFillLineSymbol;
1427 }
1428 
1430 {
1431  if ( !symbol )
1432  {
1433  return false;
1434  }
1435 
1436  if ( symbol->type() == QgsSymbolV2::Line )
1437  {
1438  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1439  if ( lineSymbol )
1440  {
1441  delete mFillLineSymbol;
1442  mFillLineSymbol = lineSymbol;
1443 
1444  return true;
1445  }
1446  }
1447  delete symbol;
1448  return false;
1449 }
1450 
1452 {
1453  return mFillLineSymbol;
1454 }
1455 
1457 {
1458  return 0;
1459 }
1460 
1462 {
1463  mDistanceUnit = unit;
1464  mLineWidthUnit = unit;
1465  mOffsetUnit = unit;
1466 }
1467 
1469 {
1471  if ( mLineWidthUnit != unit || mOffsetUnit != unit )
1472  {
1473  return QgsSymbolV2::Mixed;
1474  }
1475  return unit;
1476 }
1477 
1479 {
1481 
1482  //default values
1483  double lineAngle = 45;
1484  double distance = 5;
1485  double lineWidth = 0.5;
1486  QColor color( Qt::black );
1487  double offset = 0.0;
1488 
1489  if ( properties.contains( "lineangle" ) )
1490  {
1491  lineAngle = properties["lineangle"].toDouble();
1492  }
1493  patternLayer->setLineAngle( lineAngle );
1494 
1495  if ( properties.contains( "distance" ) )
1496  {
1497  distance = properties["distance"].toDouble();
1498  }
1499  patternLayer->setDistance( distance );
1500 
1501  if ( properties.contains( "linewidth" ) )
1502  {
1503  lineWidth = properties["linewidth"].toDouble();
1504  }
1505  patternLayer->setLineWidth( lineWidth );
1506 
1507  if ( properties.contains( "color" ) )
1508  {
1509  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
1510  }
1511  patternLayer->setColor( color );
1512 
1513  if ( properties.contains( "offset" ) )
1514  {
1515  offset = properties["offset"].toDouble();
1516  }
1517  patternLayer->setOffset( offset );
1518 
1519 
1520  if ( properties.contains( "distance_unit" ) )
1521  {
1522  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
1523  }
1524  if ( properties.contains( "line_width_unit" ) )
1525  {
1526  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
1527  }
1528  if ( properties.contains( "offset_unit" ) )
1529  {
1530  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
1531  }
1532 
1533  //data defined properties
1534  if ( properties.contains( "lineangle_expression" ) )
1535  {
1536  patternLayer->setDataDefinedProperty( "lineangle", properties["lineangle_expression"] );
1537  }
1538  if ( properties.contains( "distance_expression" ) )
1539  {
1540  patternLayer->setDataDefinedProperty( "distance", properties["distance_expression"] );
1541  }
1542  if ( properties.contains( "linewidth_expression" ) )
1543  {
1544  patternLayer->setDataDefinedProperty( "linewidth", properties["linewidth_expression"] );
1545  }
1546  if ( properties.contains( "color_expression" ) )
1547  {
1548  patternLayer->setDataDefinedProperty( "color", properties["color_expression"] );
1549  }
1550  return patternLayer;
1551 }
1552 
1554 {
1555  return "LinePatternFill";
1556 }
1557 
1558 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
1559  double lineWidth, const QColor& color )
1560 {
1561  Q_UNUSED( lineWidth );
1562  Q_UNUSED( color );
1563 
1564  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
1565 
1566  if ( !mFillLineSymbol )
1567  {
1568  return;
1569  }
1570  // We have to make a copy because marker intervals will have to be adjusted
1571  QgsLineSymbolV2* fillLineSymbol = dynamic_cast<QgsLineSymbolV2*>( mFillLineSymbol->clone() );
1572  if ( !fillLineSymbol )
1573  {
1574  return;
1575  }
1576 
1577  const QgsRenderContext& ctx = context.renderContext();
1578  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit );
1579  double outputPixelDist = distance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit );
1580  double outputPixelOffset = mOffset * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mOffsetUnit );
1581 
1582  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
1583  // For marker lines we have to get markers interval.
1584  double outputPixelBleed = 0;
1585  double outputPixelInterval = 0; // maximum interval
1586  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
1587  {
1588  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
1589  double layerBleed = layer->estimateMaxBleed();
1590  // TODO: to get real bleed we have to scale it using context and units,
1591  // unfortunately estimateMaxBleed() ignore units completely, e.g.
1592  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
1593  // offset regardless units. This has to be fixed especially
1594  // in estimateMaxBleed(), context probably has to be used.
1595  // For now, we only support milimeters
1596  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
1597  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
1598 
1599  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
1600  if ( markerLineLayer )
1601  {
1602  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit() );
1603 
1604  // There may be multiple marker lines with different intervals.
1605  // In theory we should find the least common multiple, but that could be too
1606  // big (multiplication of intervals in the worst case).
1607  // Because patterns without small common interval would look strange, we
1608  // believe that the longest interval should usually be sufficient.
1609  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
1610  }
1611  }
1612 
1613  if ( outputPixelInterval > 0 )
1614  {
1615  // We have to adjust marker intervals to integer pixel size to get
1616  // repeatable pattern.
1617  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
1618  outputPixelInterval = qRound( outputPixelInterval );
1619 
1620  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
1621  {
1622  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
1623 
1624  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
1625  if ( markerLineLayer )
1626  {
1627  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
1628  }
1629  }
1630  }
1631 
1632  //create image
1633  int height, width;
1634  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
1635  {
1636  height = outputPixelDist;
1637  width = outputPixelInterval > 0 ? outputPixelInterval : height;
1638  }
1639  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
1640  {
1641  width = outputPixelDist;
1642  height = outputPixelInterval > 0 ? outputPixelInterval : width;
1643  }
1644  else
1645  {
1646  height = qAbs( outputPixelDist / cos( lineAngle * M_PI / 180 ) ); //keep perpendicular distance between lines constant
1647  width = qAbs( height / tan( lineAngle * M_PI / 180 ) );
1648 
1649  // recalculate real angle and distance after rounding to pixels
1650  lineAngle = 180 * qAbs( atan2(( double ) height, ( double ) width ) ) / M_PI;
1651  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
1652 
1653  // Round offset to correspond to one pixel height, otherwise lines may
1654  // be shifted on tile border if offset falls close to pixel center
1655  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
1656  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
1657  }
1658 
1659  //depending on the angle, we might need to render into a larger image and use a subset of it
1660  double dx = 0;
1661  double dy = 0;
1662 
1663  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
1664  // thus we add integer multiplications of width and heigh covering the bleed
1665  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
1666 
1667  // Always buffer at least once so that center of line marker in upper right corner
1668  // does not fall outside due to representation error
1669  bufferMulti = qMax( bufferMulti, 1 );
1670 
1671  int xBuffer = width * bufferMulti;
1672  int yBuffer = height * bufferMulti;
1673  int innerWidth = width;
1674  int innerHeight = height;
1675  width += 2 * xBuffer;
1676  height += 2 * yBuffer;
1677 
1678  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
1679  {
1680  return;
1681  }
1682 
1683  QImage patternImage( width, height, QImage::Format_ARGB32 );
1684  patternImage.fill( 0 );
1685 
1686  QPointF p1, p2, p3, p4, p5, p6;
1687  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
1688  {
1689  p1 = QPointF( 0, yBuffer );
1690  p2 = QPointF( width, yBuffer );
1691  p3 = QPointF( 0, yBuffer + innerHeight );
1692  p4 = QPointF( width, yBuffer + innerHeight );
1693  }
1694  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
1695  {
1696  p1 = QPointF( xBuffer, height );
1697  p2 = QPointF( xBuffer, 0 );
1698  p3 = QPointF( xBuffer + innerWidth, height );
1699  p4 = QPointF( xBuffer + innerWidth, 0 );
1700  }
1701  else if (( lineAngle > 0 && lineAngle < 90 ) || ( lineAngle > 180 && lineAngle < 270 ) )
1702  {
1703  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
1704  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
1705  p1 = QPointF( 0, height );
1706  p2 = QPointF( width, 0 );
1707  p3 = QPointF( -dx, height - dy );
1708  p4 = QPointF( width - dx, -dy ); //p4 = QPoint( p3.x() + width, p3.y() - height );
1709  p5 = QPointF( dx, height + dy );
1710  p6 = QPointF( width + dx, dy ); //p6 = QPoint( p5.x() + width, p5.y() - height );
1711  }
1712  else if (( lineAngle < 180 ) || ( lineAngle > 270 && lineAngle < 360 ) )
1713  {
1714  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
1715  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
1716  p1 = QPointF( width, height );
1717  p2 = QPointF( 0, 0 );
1718  p5 = QPointF( width + dx, height - dy );
1719  p6 = QPointF( p5.x() - width, p5.y() - height ); //p6 = QPoint( dx, -dy );
1720  p3 = QPointF( width - dx, height + dy );
1721  p4 = QPointF( p3.x() - width, p3.y() - height ); //p4 = QPoint( -dx, dy );
1722  }
1723 
1724  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
1725  {
1726  QPointF tempPt;
1727  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
1728  p3 = QPointF( tempPt.x(), tempPt.y() );
1729  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
1730  p4 = QPointF( tempPt.x(), tempPt.y() );
1731  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
1732  p5 = QPointF( tempPt.x(), tempPt.y() );
1733  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
1734  p6 = QPointF( tempPt.x(), tempPt.y() );
1735 
1736  //update p1, p2 last
1737  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
1738  p1 = QPointF( tempPt.x(), tempPt.y() );
1739  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
1740  p2 = QPointF( tempPt.x(), tempPt.y() );;
1741  }
1742 
1743  QPainter p( &patternImage );
1744 
1745 #if 0
1746  // DEBUG: Draw rectangle
1747  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
1748  QPen pen( QColor( Qt::black ) );
1749  pen.setWidthF( 0.1 );
1750  pen.setCapStyle( Qt::FlatCap );
1751  p.setPen( pen );
1752 
1753  // To see this rectangle, comment buffer cut below.
1754  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
1755  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 ) ;
1756  p.drawPolygon( polygon );
1757 
1758  polygon = QPolygon() << QPoint( xBuffer, yBuffer ) << QPoint( width - xBuffer - 1, yBuffer ) << QPoint( width - xBuffer - 1, height - yBuffer - 1 ) << QPoint( xBuffer, height - yBuffer - 1 ) << QPoint( xBuffer, yBuffer ) ;
1759  p.drawPolygon( polygon );
1760 #endif
1761 
1762  // Use antialiasing because without antialiasing lines are rendered to the
1763  // right and below the mathematically defined points (not symetrical)
1764  // and such tiles become useless for are filling
1765  p.setRenderHint( QPainter::Antialiasing, true );
1766 
1767  // line rendering needs context for drawing on patternImage
1768  QgsRenderContext lineRenderContext;
1769  lineRenderContext.setPainter( &p );
1770  lineRenderContext.setRasterScaleFactor( 1.0 );
1771  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
1773  lineRenderContext.setMapToPixel( mtp );
1774  lineRenderContext.setForceVectorOutput( false );
1775 
1776  fillLineSymbol->startRender( lineRenderContext );
1777 
1778  QVector<QPolygonF> polygons;
1779  polygons.append( QPolygonF() << p1 << p2 );
1780  polygons.append( QPolygonF() << p3 << p4 );
1781  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
1782  {
1783  polygons.append( QPolygonF() << p5 << p6 );
1784  }
1785 
1786  foreach ( QPolygonF polygon, polygons )
1787  {
1788  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
1789  }
1790 
1791  fillLineSymbol->stopRender( lineRenderContext );
1792  p.end();
1793 
1794  // Cut off the buffer
1795  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
1796 
1797  //set image to mBrush
1798  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1799  {
1800  QImage transparentImage = patternImage.copy();
1801  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1802  brush.setTextureImage( transparentImage );
1803  }
1804  else
1805  {
1806  brush.setTextureImage( patternImage );
1807  }
1808 
1809  QTransform brushTransform;
1810  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1811  brush.setTransform( brushTransform );
1812 
1813  delete fillLineSymbol;
1814 }
1815 
1817 {
1819 
1820  if ( mFillLineSymbol )
1821  {
1823  }
1824 
1825  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
1826 }
1827 
1829 {
1830 }
1831 
1833 {
1834  QgsStringMap map;
1835  map.insert( "lineangle", QString::number( mLineAngle ) );
1836  map.insert( "distance", QString::number( mDistance ) );
1837  map.insert( "linewidth", QString::number( mLineWidth ) );
1838  map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mColor ) );
1839  map.insert( "offset", QString::number( mOffset ) );
1840  map.insert( "distance_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit ) );
1841  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
1842  map.insert( "offset_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ) );
1844  return map;
1845 }
1846 
1848 {
1850  if ( mFillLineSymbol )
1851  {
1852  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
1853  }
1854  clonedLayer->setDistanceUnit( mDistanceUnit );
1855  clonedLayer->setLineWidthUnit( mLineWidthUnit );
1856  clonedLayer->setOffsetUnit( mOffsetUnit );
1857  copyDataDefinedProperties( clonedLayer );
1858  return clonedLayer;
1859 }
1860 
1861 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1862 {
1863  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
1864  if ( !props.value( "uom", "" ).isEmpty() )
1865  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1866  element.appendChild( symbolizerElem );
1867 
1868  // <Geometry>
1869  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1870 
1871  QDomElement fillElem = doc.createElement( "se:Fill" );
1872  symbolizerElem.appendChild( fillElem );
1873 
1874  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1875  fillElem.appendChild( graphicFillElem );
1876 
1877  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1878  graphicFillElem.appendChild( graphicElem );
1879 
1880  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), mColor, Qt::SolidLine, mLineWidth, mDistance );
1881 
1882  // <Rotation>
1883  QString angleFunc;
1884  bool ok;
1885  double angle = props.value( "angle", "0" ).toDouble( &ok );
1886  if ( !ok )
1887  {
1888  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
1889  }
1890  else if ( angle + mLineAngle != 0 )
1891  {
1892  angleFunc = QString::number( angle + mLineAngle );
1893  }
1894  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1895 
1896  // <se:Displacement>
1897  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
1898  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
1899 
1900  if ( mFillLineSymbol )
1901  {
1902  mFillLineSymbol->toSld( doc, element, props );
1903  }
1904 }
1905 
1907 {
1908  QString featureStyle;
1909  featureStyle.append( "Brush(" );
1910  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
1911  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
1912  featureStyle.append( ",id:\"ogr-brush-2\"" );
1913  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
1914  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
1915  featureStyle.append( ",dx:0mm" );
1916  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
1917  featureStyle.append( ")" );
1918  return featureStyle;
1919 }
1920 
1922 {
1923  QgsExpression* lineAngleExpression = expression( "lineangle" );
1924  QgsExpression* distanceExpression = expression( "distance" );
1925  QgsExpression* lineWidthExpression = expression( "linewidth" );
1926  QgsExpression* colorExpression = expression( "color" );
1927  if ( !lineAngleExpression && !distanceExpression && !lineWidthExpression && !colorExpression )
1928  {
1929  return; //no data defined settings
1930  }
1931 
1932  double lineAngle = mLineAngle;
1933  if ( lineAngleExpression )
1934  {
1935  lineAngle = lineAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1936  }
1937  double distance = mDistance;
1938  if ( distanceExpression )
1939  {
1940  distance = distanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1941  }
1942  double lineWidth = mLineWidth;
1943  if ( lineWidthExpression )
1944  {
1945  lineWidth = lineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1946  }
1947  QColor color = mColor;
1948  if ( colorExpression )
1949  {
1950  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1951  }
1952  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
1953 }
1954 
1956 {
1957  QgsDebugMsg( "Entered." );
1958 
1959  QString name;
1960  QColor fillColor, lineColor;
1961  double size, lineWidth;
1962  Qt::PenStyle lineStyle;
1963 
1964  QDomElement fillElem = element.firstChildElement( "Fill" );
1965  if ( fillElem.isNull() )
1966  return NULL;
1967 
1968  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1969  if ( graphicFillElem.isNull() )
1970  return NULL;
1971 
1972  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1973  if ( graphicElem.isNull() )
1974  return NULL;
1975 
1976  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
1977  return NULL;
1978 
1979  if ( name != "horline" )
1980  return NULL;
1981 
1982  double angle = 0.0;
1983  QString angleFunc;
1984  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1985  {
1986  bool ok;
1987  double d = angleFunc.toDouble( &ok );
1988  if ( ok )
1989  angle = d;
1990  }
1991 
1992  double offset = 0.0;
1993  QPointF vectOffset;
1994  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
1995  {
1996  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
1997  }
1998 
2000  sl->setColor( lineColor );
2001  sl->setLineWidth( lineWidth );
2002  sl->setLineAngle( angle );
2003  sl->setOffset( offset );
2004  sl->setDistance( size );
2005 
2006  // try to get the outline
2007  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2008  if ( !strokeElem.isNull() )
2009  {
2011  if ( l )
2012  {
2013  QgsSymbolLayerV2List layers;
2014  layers.append( l );
2015  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2016  }
2017  }
2018 
2019  return sl;
2020 }
2021 
2022 
2024 
2026  mDistanceXUnit( QgsSymbolV2::MM ), mDistanceY( 15 ), mDistanceYUnit( QgsSymbolV2::MM ), mDisplacementX( 0 ), mDisplacementXUnit( QgsSymbolV2::MM ),
2027  mDisplacementY( 0 ), mDisplacementYUnit( QgsSymbolV2::MM )
2028 {
2029  mDistanceX = 15;
2030  mDistanceY = 15;
2031  mDisplacementX = 0;
2032  mDisplacementY = 0;
2034  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
2035 }
2036 
2038 {
2039 }
2040 
2042 {
2043  mDistanceXUnit = unit;
2044  mDistanceYUnit = unit;
2045  mDisplacementXUnit = unit;
2046  mDisplacementYUnit = unit;
2047 }
2048 
2050 {
2052  if ( mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
2053  {
2054  return QgsSymbolV2::Mixed;
2055  }
2056  return unit;
2057 }
2058 
2060 {
2062  if ( properties.contains( "distance_x" ) )
2063  {
2064  layer->setDistanceX( properties["distance_x"].toDouble() );
2065  }
2066  if ( properties.contains( "distance_y" ) )
2067  {
2068  layer->setDistanceY( properties["distance_y"].toDouble() );
2069  }
2070  if ( properties.contains( "displacement_x" ) )
2071  {
2072  layer->setDisplacementX( properties["displacement_x"].toDouble() );
2073  }
2074  if ( properties.contains( "displacement_y" ) )
2075  {
2076  layer->setDisplacementY( properties["displacement_y"].toDouble() );
2077  }
2078 
2079  if ( properties.contains( "distance_x_unit" ) )
2080  {
2081  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
2082  }
2083  if ( properties.contains( "distance_y_unit" ) )
2084  {
2085  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
2086  }
2087  if ( properties.contains( "displacement_x_unit" ) )
2088  {
2089  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
2090  }
2091  if ( properties.contains( "displacement_y_unit" ) )
2092  {
2093  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
2094  }
2095 
2096  //data defined properties
2097  if ( properties.contains( "distance_x_expression" ) )
2098  {
2099  layer->setDataDefinedProperty( "distance_x", properties["distance_x_expression"] );
2100  }
2101  if ( properties.contains( "distance_y_expression" ) )
2102  {
2103  layer->setDataDefinedProperty( "distance_y", properties["distance_y_expression"] );
2104  }
2105  if ( properties.contains( "displacement_x_expression" ) )
2106  {
2107  layer->setDataDefinedProperty( "displacement_x", properties["displacement_x_expression"] );
2108  }
2109  if ( properties.contains( "displacement_y_expression" ) )
2110  {
2111  layer->setDataDefinedProperty( "displacement_y", properties["displacement_y_expression"] );
2112  }
2113  return layer;
2114 }
2115 
2117 {
2118  return "PointPatternFill";
2119 }
2120 
2121 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
2122  double displacementX, double displacementY )
2123 {
2124  //render 3 rows and columns in one go to easily incorporate displacement
2125  const QgsRenderContext& ctx = context.renderContext();
2126  double width = distanceX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceXUnit ) * 2.0;
2127  double height = distanceY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceYUnit ) * 2.0;
2128 
2129  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2130  {
2131  QImage img;
2132  brush.setTextureImage( img );
2133  return;
2134  }
2135 
2136  QImage patternImage( width, height, QImage::Format_ARGB32 );
2137  patternImage.fill( 0 );
2138 
2139  if ( mMarkerSymbol )
2140  {
2141  QPainter p( &patternImage );
2142 
2143  //marker rendering needs context for drawing on patternImage
2144  QgsRenderContext pointRenderContext;
2145  pointRenderContext.setPainter( &p );
2146  pointRenderContext.setRasterScaleFactor( 1.0 );
2147  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2149  pointRenderContext.setMapToPixel( mtp );
2150  pointRenderContext.setForceVectorOutput( false );
2151 
2152  mMarkerSymbol->startRender( pointRenderContext );
2153 
2154  //render corner points
2155  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
2156  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
2157  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
2158  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
2159 
2160  //render displaced points
2161  double displacementPixelX = displacementX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementXUnit );
2162  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit );
2163  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
2164  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
2165  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
2166  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
2167  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
2168 
2169  mMarkerSymbol->stopRender( pointRenderContext );
2170  }
2171 
2172  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2173  {
2174  QImage transparentImage = patternImage.copy();
2175  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2176  brush.setTextureImage( transparentImage );
2177  }
2178  else
2179  {
2180  brush.setTextureImage( patternImage );
2181  }
2182  QTransform brushTransform;
2183  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2184  brush.setTransform( brushTransform );
2185 }
2186 
2188 {
2190 
2191  if ( mOutline )
2192  {
2193  mOutline->startRender( context.renderContext() );
2194  }
2195  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
2196 }
2197 
2199 {
2200  if ( mOutline )
2201  {
2202  mOutline->stopRender( context.renderContext() );
2203  }
2204 }
2205 
2207 {
2208  QgsStringMap propertyMap;
2209  propertyMap["distance_x"] = QString::number( mDistanceX );
2210  propertyMap["distance_y"] = QString::number( mDistanceY );
2211  propertyMap["displacement_x"] = QString::number( mDisplacementX );
2212  propertyMap["displacement_y"] = QString::number( mDisplacementY );
2213  propertyMap["distance_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit );
2214  propertyMap["distance_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit );
2215  propertyMap["displacement_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit );
2216  propertyMap["displacement_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit );
2217  saveDataDefinedProperties( propertyMap );
2218  return propertyMap;
2219 }
2220 
2222 {
2224  if ( mMarkerSymbol )
2225  {
2226  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
2227  }
2228  copyDataDefinedProperties( clonedLayer );
2229  return clonedLayer;
2230 }
2231 
2232 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2233 {
2234  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
2235  {
2236  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2237  if ( !props.value( "uom", "" ).isEmpty() )
2238  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2239  element.appendChild( symbolizerElem );
2240 
2241  // <Geometry>
2242  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2243 
2244  QDomElement fillElem = doc.createElement( "se:Fill" );
2245  symbolizerElem.appendChild( fillElem );
2246 
2247  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2248  fillElem.appendChild( graphicFillElem );
2249 
2250  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
2251  QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
2252  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
2253  symbolizerElem.appendChild( distanceElem );
2254 
2256  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
2257  if ( !markerLayer )
2258  {
2259  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
2260  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
2261  }
2262  else
2263  {
2264  markerLayer->writeSldMarker( doc, graphicFillElem, props );
2265  }
2266  }
2267 }
2268 
2270 {
2271  Q_UNUSED( element );
2272  return NULL;
2273 }
2274 
2276 {
2277  if ( !symbol )
2278  {
2279  return false;
2280  }
2281 
2282  if ( symbol->type() == QgsSymbolV2::Marker )
2283  {
2284  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
2285  delete mMarkerSymbol;
2286  mMarkerSymbol = markerSymbol;
2287  }
2288  return true;
2289 }
2290 
2292 {
2293  QgsExpression* distanceXExpression = expression( "distance_x" );
2294  QgsExpression* distanceYExpression = expression( "distance_y" );
2295  QgsExpression* displacementXExpression = expression( "displacement_x" );
2296  QgsExpression* displacementYExpression = expression( "displacement_y" );
2297 
2298 #if 0
2299  // TODO: enable but check also if mMarkerSymbol has data defined properties
2300  if ( !distanceXExpression && !distanceYExpression && !displacementXExpression && !displacementYExpression )
2301  {
2302  return;
2303  }
2304 #endif
2305 
2306  double distanceX = mDistanceX;
2307  if ( distanceXExpression )
2308  {
2309  distanceX = distanceXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2310  }
2311  double distanceY = mDistanceY;
2312  if ( distanceYExpression )
2313  {
2314  distanceY = distanceYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2315  }
2316  double displacementX = mDisplacementX;
2317  if ( displacementXExpression )
2318  {
2319  displacementX = displacementXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2320  }
2321  double displacementY = mDisplacementY;
2322  if ( displacementYExpression )
2323  {
2324  displacementY = displacementYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2325  }
2326  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
2327 }
2328 
2330 {
2331  return 0;
2332 }
2333 
2335 {
2336  QSet<QString> attributes = QgsSymbolLayerV2::usedAttributes();
2337 
2338  if ( mMarkerSymbol )
2339  attributes.unite( mMarkerSymbol->usedAttributes() );
2340 
2341  return attributes;
2342 }
2343 
2345 
2346 
2348 {
2350 }
2351 
2353 {
2354  delete mMarker;
2355 }
2356 
2358 {
2359  Q_UNUSED( properties );
2360  return new QgsCentroidFillSymbolLayerV2();
2361 }
2362 
2364 {
2365  return "CentroidFill";
2366 }
2367 
2368 void QgsCentroidFillSymbolLayerV2::setColor( const QColor& color )
2369 {
2370  mMarker->setColor( color );
2371  mColor = color;
2372 }
2373 
2375 {
2376  mMarker->setAlpha( context.alpha() );
2377  mMarker->startRender( context.renderContext() );
2378 }
2379 
2381 {
2382  mMarker->stopRender( context.renderContext() );
2383 }
2384 
2385 void QgsCentroidFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
2386 {
2387  Q_UNUSED( rings );
2388 
2389  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
2390  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
2391 }
2392 
2394 {
2395  return QgsStringMap();
2396 }
2397 
2399 {
2401  x->setSubSymbol( mMarker->clone() );
2402  return x;
2403 }
2404 
2405 void QgsCentroidFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2406 {
2407  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
2408  // used with PointSymbolizer, then the semantic is to use the centroid
2409  // of the geometry, or any similar representative point.
2410  mMarker->toSld( doc, element, props );
2411 }
2412 
2414 {
2415  QgsDebugMsg( "Entered." );
2416 
2418  if ( !l )
2419  return NULL;
2420 
2421  QgsSymbolLayerV2List layers;
2422  layers.append( l );
2423  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
2424 
2426  x->setSubSymbol( marker );
2427  return x;
2428 }
2429 
2430 
2432 {
2433  return mMarker;
2434 }
2435 
2437 {
2438  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
2439  {
2440  delete symbol;
2441  return false;
2442  }
2443 
2444  delete mMarker;
2445  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
2446  mColor = mMarker->color();
2447  return true;
2448 }
2449 
2451 {
2452  QSet<QString> attributes;
2453 
2454  attributes.unite( QgsSymbolLayerV2::usedAttributes() );
2455 
2456  if ( mMarker )
2457  attributes.unite( mMarker->usedAttributes() );
2458 
2459  return attributes;
2460 }
2461 
2463 {
2464  if ( mMarker )
2465  {
2466  return mMarker->outputUnit();
2467  }
2468  return QgsSymbolV2::Mixed; //mOutputUnit;
2469 }