QGIS API Documentation  2.3.0-Master
 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 
16 #include "qgsfillsymbollayerv2.h"
17 #include "qgslinesymbollayerv2.h"
18 #include "qgsmarkersymbollayerv2.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsdxfexport.h"
21 #include "qgsexpression.h"
22 #include "qgsrendercontext.h"
23 #include "qgsproject.h"
24 #include "qgssvgcache.h"
25 #include "qgslogger.h"
26 #include "qgsvectorcolorrampv2.h"
27 
28 #include <QPainter>
29 #include <QFile>
30 #include <QSvgRenderer>
31 #include <QDomDocument>
32 #include <QDomElement>
33 
34 QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( QColor color, Qt::BrushStyle style, QColor borderColor, Qt::PenStyle borderStyle, double borderWidth,
35  Qt::PenJoinStyle penJoinStyle ) :
36  mBrushStyle( style ),
37  mBorderColor( borderColor ),
38  mBorderStyle( borderStyle ),
39  mBorderWidth( borderWidth ),
40  mBorderWidthUnit( QgsSymbolV2::MM ),
41  mPenJoinStyle( penJoinStyle ),
42  mOffsetUnit( QgsSymbolV2::MM )
43 {
44  mColor = color;
45 }
46 
48 {
49  mBorderWidthUnit = unit;
50  mOffsetUnit = unit;
51 }
52 
54 {
56  if ( mOffsetUnit != unit )
57  {
58  return QgsSymbolV2::Mixed;
59  }
60  return unit;
61 }
62 
63 void QgsSimpleFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QBrush& brush, QPen& pen, QPen& selPen )
64 {
65  QgsExpression* colorExpression = expression( "color" );
66  if ( colorExpression )
67  {
68  brush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
69  }
70  QgsExpression* colorBorderExpression = expression( "color_border" );
71  if ( colorBorderExpression )
72  {
73  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
74  }
75  QgsExpression* widthBorderExpression = expression( "width_border" );
76  if ( widthBorderExpression )
77  {
78  double width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
80  pen.setWidthF( width );
81  selPen.setWidthF( width );
82  }
83 }
84 
85 
87 {
89  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
93  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
94  QPointF offset;
95 
96  if ( props.contains( "color" ) )
97  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
98  if ( props.contains( "style" ) )
99  style = QgsSymbolLayerV2Utils::decodeBrushStyle( props["style"] );
100  if ( props.contains( "color_border" ) )
101  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
102  if ( props.contains( "style_border" ) )
103  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["style_border"] );
104  if ( props.contains( "width_border" ) )
105  borderWidth = props["width_border"].toDouble();
106  if ( props.contains( "offset" ) )
107  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
108  if ( props.contains( "joinstyle" ) )
109  penJoinStyle = QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] );
110 
111  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, style, borderColor, borderStyle, borderWidth, penJoinStyle );
112  sl->setOffset( offset );
113  if ( props.contains( "border_width_unit" ) )
114  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["border_width_unit"] ) );
115  if ( props.contains( "offset_unit" ) )
116  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
117 
118  if ( props.contains( "color_expression" ) )
119  {
120  sl->setDataDefinedProperty( "color", props["color_expression"] );
121  }
122  if ( props.contains( "color_border_expression" ) )
123  {
124  sl->setDataDefinedProperty( "color_border", props["color_border_expression"] );
125  }
126  if ( props.contains( "width_border_expression" ) )
127  {
128  sl->setDataDefinedProperty( "width_border", props["width_border_expression"] );
129  }
130  return sl;
131 }
132 
133 
135 {
136  return "SimpleFill";
137 }
138 
140 {
141  QColor fillColor = mColor;
142  fillColor.setAlphaF( context.alpha() * mColor.alphaF() );
143  mBrush = QBrush( fillColor, mBrushStyle );
144 
145  // scale brush content for printout
147  if ( rasterScaleFactor != 1.0 )
148  {
149  mBrush.setMatrix( QMatrix().scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ) );
150  }
151 
152  QColor selColor = context.renderContext().selectionColor();
153  QColor selPenColor = selColor == mColor ? selColor : mBorderColor;
154  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
155  mSelBrush = QBrush( selColor );
156  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
157  // this would mean symbols with "no fill" look the same whether or not they are selected
158  if ( selectFillStyle )
159  mSelBrush.setStyle( mBrushStyle );
160 
161  QColor borderColor = mBorderColor;
162  borderColor.setAlphaF( context.alpha() * mBorderColor.alphaF() );
163  mPen = QPen( borderColor );
164  mSelPen = QPen( selPenColor );
165  mPen.setStyle( mBorderStyle );
167  mPen.setJoinStyle( mPenJoinStyle );
168  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
169 }
170 
172 {
173  Q_UNUSED( context );
174 }
175 
176 void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
177 {
178  QPainter* p = context.renderContext().painter();
179  if ( !p )
180  {
181  return;
182  }
183 
185 
186  p->setBrush( context.selected() ? mSelBrush : mBrush );
187  p->setPen( context.selected() ? mSelPen : mPen );
188 
189  QPointF offset;
190  if ( !mOffset.isNull() )
191  {
194  p->translate( offset );
195  }
196 
197  _renderPolygon( p, points, rings, context );
198 
199  if ( !mOffset.isNull() )
200  {
201  p->translate( -offset );
202  }
203 }
204 
206 {
207  QgsStringMap map;
208  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
210  map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
211  map["style_border"] = QgsSymbolLayerV2Utils::encodePenStyle( mBorderStyle );
212  map["width_border"] = QString::number( mBorderWidth );
213  map["border_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mBorderWidthUnit );
215  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
218  return map;
219 }
220 
222 {
224  sl->setOffset( mOffset );
225  sl->setOffsetUnit( mOffsetUnit );
228  return sl;
229 }
230 
231 void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
232 {
233  if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
234  return;
235 
236  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
237  if ( !props.value( "uom", "" ).isEmpty() )
238  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
239  element.appendChild( symbolizerElem );
240 
241  // <Geometry>
242  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
243 
244  if ( mBrushStyle != Qt::NoBrush )
245  {
246  // <Fill>
247  QDomElement fillElem = doc.createElement( "se:Fill" );
248  symbolizerElem.appendChild( fillElem );
250  }
251 
252  if ( mBorderStyle != Qt::NoPen )
253  {
254  // <Stroke>
255  QDomElement strokeElem = doc.createElement( "se:Stroke" );
256  symbolizerElem.appendChild( strokeElem );
258  }
259 
260  // <se:Displacement>
262 }
263 
264 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
265 {
266  //brush
267  QString symbolStyle;
268  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStyleBrush( mColor ) );
269  symbolStyle.append( ";" );
270  //pen
271  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor, mPenJoinStyle ) );
272  return symbolStyle;
273 }
274 
276 {
277  QgsDebugMsg( "Entered." );
278 
279  QColor color, borderColor;
280  Qt::BrushStyle fillStyle;
281  Qt::PenStyle borderStyle;
282  double borderWidth;
283 
284  QDomElement fillElem = element.firstChildElement( "Fill" );
285  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
286 
287  QDomElement strokeElem = element.firstChildElement( "Stroke" );
288  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
289 
290  QPointF offset;
292 
293  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
294  sl->setOffset( offset );
295  return sl;
296 }
297 
299 {
300  double penBleed = mBorderStyle == Qt::NoPen ? 0 : ( mBorderWidth / 2.0 );
301  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
302  return penBleed + offsetBleed;
303 }
304 
305 double QgsSimpleFillSymbolLayerV2::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
306 {
307  double width = mBorderWidth;
308  QgsExpression* widthBorderExpression = expression( "width_border" );
309  if ( widthBorderExpression )
310  {
311  width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
312  }
313  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), mBorderWidthUnit, e.mapUnits() );
314 }
315 
317 {
318  if ( mBrushStyle == Qt::NoBrush )
319  {
320  QgsExpression* colorBorderExpression = expression( "color_border" );
321  if ( colorBorderExpression )
322  {
323  return QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
324  }
325  return mBorderColor;
326  }
327  else
328  {
329  QgsExpression* colorExpression = expression( "color" );
330  if ( colorExpression )
331  {
332  return QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
333  }
334  return mColor;
335  }
336 }
337 
339 {
340  return mBorderStyle;
341 }
342 
343 //QgsGradientFillSymbolLayer
344 
346  GradientColorType colorType, GradientType gradientType,
347  GradientCoordinateMode coordinateMode, GradientSpread spread )
348  : mGradientColorType( colorType ),
349  mGradientRamp( NULL ),
350  mGradientType( gradientType ),
351  mCoordinateMode( coordinateMode ),
352  mGradientSpread( spread ),
353  mReferencePoint1( QPointF( 0.5, 0 ) ),
354  mReferencePoint1IsCentroid( false ),
355  mReferencePoint2( QPointF( 0.5, 1 ) ),
356  mReferencePoint2IsCentroid( false ),
357  mAngle( 0 ),
358  mOffsetUnit( QgsSymbolV2::MM )
359 {
360  mColor = color;
361  mColor2 = color2;
362 }
363 
365 {
366  delete mGradientRamp;
367 }
368 
370 {
371  //default to a two-color, linear gradient with feature mode and pad spreading
376  //default to gradient from the default fill color to white
377  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
378  QPointF referencePoint1 = QPointF( 0.5, 0 );
379  bool refPoint1IsCentroid = false;
380  QPointF referencePoint2 = QPointF( 0.5, 1 );
381  bool refPoint2IsCentroid = false;
382  double angle = 0;
383  QPointF offset;
384 
385  //update gradient properties from props
386  if ( props.contains( "type" ) )
387  type = ( GradientType )props["type"].toInt();
388  if ( props.contains( "coordinate_mode" ) )
389  coordinateMode = ( GradientCoordinateMode )props["coordinate_mode"].toInt();
390  if ( props.contains( "spread" ) )
391  gradientSpread = ( GradientSpread )props["spread"].toInt();
392  if ( props.contains( "color_type" ) )
393  colorType = ( GradientColorType )props["color_type"].toInt();
394  if ( props.contains( "gradient_color" ) )
395  color = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color"] );
396  if ( props.contains( "gradient_color2" ) )
397  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
398  if ( props.contains( "reference_point1" ) )
399  referencePoint1 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point1"] );
400  if ( props.contains( "reference_point1_iscentroid" ) )
401  refPoint1IsCentroid = props["reference_point1_iscentroid"].toInt();
402  if ( props.contains( "reference_point2" ) )
403  referencePoint2 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point2"] );
404  if ( props.contains( "reference_point2_iscentroid" ) )
405  refPoint2IsCentroid = props["reference_point2_iscentroid"].toInt();
406  if ( props.contains( "angle" ) )
407  angle = props["angle"].toDouble();
408  if ( props.contains( "offset" ) )
409  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
410 
411  //attempt to create color ramp from props
413 
414  //create a new gradient fill layer with desired properties
415  QgsGradientFillSymbolLayerV2* sl = new QgsGradientFillSymbolLayerV2( color, color2, colorType, type, coordinateMode, gradientSpread );
416  sl->setOffset( offset );
417  if ( props.contains( "offset_unit" ) )
418  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
419  sl->setReferencePoint1( referencePoint1 );
420  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
421  sl->setReferencePoint2( referencePoint2 );
422  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
423  sl->setAngle( angle );
424  if ( gradientRamp )
425  sl->setColorRamp( gradientRamp );
426 
427  //data defined symbology expressions
428  if ( props.contains( "color_expression" ) )
429  sl->setDataDefinedProperty( "color", props["color_expression"] );
430  if ( props.contains( "color2_expression" ) )
431  sl->setDataDefinedProperty( "color2", props["color2_expression"] );
432  if ( props.contains( "angle_expression" ) )
433  sl->setDataDefinedProperty( "angle", props["angle_expression"] );
434  if ( props.contains( "gradient_type_expression" ) )
435  sl->setDataDefinedProperty( "gradient_type", props["gradient_type_expression"] );
436  if ( props.contains( "coordinate_mode_expression" ) )
437  sl->setDataDefinedProperty( "coordinate_mode", props["coordinate_mode_expression"] );
438  if ( props.contains( "spread_expression" ) )
439  sl->setDataDefinedProperty( "spread", props["spread_expression"] );
440  if ( props.contains( "reference1_x_expression" ) )
441  sl->setDataDefinedProperty( "reference1_x", props["reference1_x_expression"] );
442  if ( props.contains( "reference1_y_expression" ) )
443  sl->setDataDefinedProperty( "reference1_y", props["reference1_y_expression"] );
444  if ( props.contains( "reference1_iscentroid_expression" ) )
445  sl->setDataDefinedProperty( "reference1_iscentroid", props["reference1_iscentroid_expression"] );
446  if ( props.contains( "reference2_x_expression" ) )
447  sl->setDataDefinedProperty( "reference2_x", props["reference2_x_expression"] );
448  if ( props.contains( "reference2_y_expression" ) )
449  sl->setDataDefinedProperty( "reference2_y", props["reference2_y_expression"] );
450  if ( props.contains( "reference2_iscentroid_expression" ) )
451  sl->setDataDefinedProperty( "reference2_iscentroid", props["reference2_iscentroid_expression"] );
452 
453  return sl;
454 }
455 
457 {
458  delete mGradientRamp;
459  mGradientRamp = ramp;
460 }
461 
463 {
464  return "GradientFill";
465 }
466 
468 {
469  //first gradient color
470  QgsExpression* colorExpression = expression( "color" );
471  QColor color = mColor;
472  if ( colorExpression )
473  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
474 
475  //second gradient color
476  QgsExpression* colorExpression2 = expression( "color2" );
477  QColor color2 = mColor2;
478  if ( colorExpression2 )
479  color2 = QgsSymbolLayerV2Utils::decodeColor( colorExpression2->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
480 
481  //gradient rotation angle
482  QgsExpression* angleExpression = expression( "angle" );
483  double angle = mAngle;
484  if ( angleExpression )
485  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
486 
487  //gradient type
488  QgsExpression* typeExpression = expression( "gradient_type" );
490  if ( typeExpression )
491  {
492  QString currentType = typeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
493  if ( currentType == QObject::tr( "linear" ) )
494  {
496  }
497  else if ( currentType == QObject::tr( "radial" ) )
498  {
500  }
501  else if ( currentType == QObject::tr( "conical" ) )
502  {
504  }
505  else
506  {
507  //default to linear
509  }
510  }
511 
512  //coordinate mode
513  QgsExpression* coordModeExpression = expression( "coordinate_mode" );
515  if ( coordModeExpression )
516  {
517  QString currentCoordMode = coordModeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
518  if ( currentCoordMode == QObject::tr( "feature" ) )
519  {
520  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
521  }
522  else if ( currentCoordMode == QObject::tr( "viewport" ) )
523  {
525  }
526  else
527  {
528  //default to feature mode
529  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
530  }
531  }
532 
533  //gradient spread
534  QgsExpression* spreadExpression = expression( "spread" );
536  if ( spreadExpression )
537  {
538  QString currentSpread = spreadExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
539  if ( currentSpread == QObject::tr( "pad" ) )
540  {
542  }
543  else if ( currentSpread == QObject::tr( "repeat" ) )
544  {
546  }
547  else if ( currentSpread == QObject::tr( "reflect" ) )
548  {
550  }
551  else
552  {
553  //default to pad spread
555  }
556  }
557 
558  //reference point 1 x & y
559  QgsExpression* ref1XExpression = expression( "reference1_x" );
560  double refPoint1X = mReferencePoint1.x();
561  if ( ref1XExpression )
562  refPoint1X = ref1XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
563  QgsExpression* ref1YExpression = expression( "reference1_y" );
564  double refPoint1Y = mReferencePoint1.y();
565  if ( ref1YExpression )
566  refPoint1Y = ref1YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
567  QgsExpression* ref1IsCentroidExpression = expression( "reference1_iscentroid" );
568  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
569  if ( ref1IsCentroidExpression )
570  refPoint1IsCentroid = ref1IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
571 
572  //reference point 2 x & y
573  QgsExpression* ref2XExpression = expression( "reference2_x" );
574  double refPoint2X = mReferencePoint2.x();
575  if ( ref2XExpression )
576  refPoint2X = ref2XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
577  QgsExpression* ref2YExpression = expression( "reference2_y" );
578  double refPoint2Y = mReferencePoint2.y();
579  if ( ref2YExpression )
580  refPoint2Y = ref2YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
581  QgsExpression* ref2IsCentroidExpression = expression( "reference2_iscentroid" );
582  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
583  if ( ref2IsCentroidExpression )
584  refPoint2IsCentroid = ref2IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
585 
586  if ( refPoint1IsCentroid || refPoint2IsCentroid )
587  {
588  //either the gradient is starting or ending at a centroid, so calculate it
589  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
590  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
591  QRectF bbox = points.boundingRect();
592  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
593  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
594 
595  if ( refPoint1IsCentroid )
596  {
597  refPoint1X = centroidX;
598  refPoint1Y = centroidY;
599  }
600  if ( refPoint2IsCentroid )
601  {
602  refPoint2X = centroidX;
603  refPoint2Y = centroidY;
604  }
605  }
606 
607  //update gradient with data defined values
608  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
609  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
610 }
611 
612 QPointF QgsGradientFillSymbolLayerV2::rotateReferencePoint( const QPointF & refPoint, double angle )
613 {
614  //rotate a reference point by a specified angle around the point (0.5, 0.5)
615 
616  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
617  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
618  //rotate this line by the current rotation angle
619  refLine.setAngle( refLine.angle() + angle );
620  //get new end point of line
621  QPointF rotatedReferencePoint = refLine.p2();
622  //make sure coords of new end point is within [0, 1]
623  if ( rotatedReferencePoint.x() > 1 )
624  rotatedReferencePoint.setX( 1 );
625  if ( rotatedReferencePoint.x() < 0 )
626  rotatedReferencePoint.setX( 0 );
627  if ( rotatedReferencePoint.y() > 1 )
628  rotatedReferencePoint.setY( 1 );
629  if ( rotatedReferencePoint.y() < 0 )
630  rotatedReferencePoint.setY( 0 );
631 
632  return rotatedReferencePoint;
633 }
634 
636  const QColor &color, const QColor &color2, const GradientColorType &gradientColorType,
637  QgsVectorColorRampV2 *gradientRamp, const GradientType &gradientType,
638  const GradientCoordinateMode &coordinateMode, const GradientSpread &gradientSpread,
639  const QPointF &referencePoint1, const QPointF &referencePoint2, const double angle )
640 {
641  //update alpha of gradient colors
642  QColor fillColor = color;
643  fillColor.setAlphaF( context.alpha() * fillColor.alphaF() );
644  QColor fillColor2 = color2;
645  fillColor2.setAlphaF( context.alpha() * fillColor2.alphaF() );
646 
647  //rotate reference points
648  QPointF rotatedReferencePoint1 = angle != 0 ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
649  QPointF rotatedReferencePoint2 = angle != 0 ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
650 
651  //create a QGradient with the desired properties
652  QGradient gradient;
653  switch ( gradientType )
654  {
656  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
657  break;
659  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
660  break;
662  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
663  break;
664  }
665  switch ( coordinateMode )
666  {
668  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
669  break;
671  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
672  break;
673  }
674  switch ( gradientSpread )
675  {
677  gradient.setSpread( QGradient::PadSpread );
678  break;
680  gradient.setSpread( QGradient::ReflectSpread );
681  break;
683  gradient.setSpread( QGradient::RepeatSpread );
684  break;
685  }
686 
687  //add stops to gradient
688  if ( gradientColorType == QgsGradientFillSymbolLayerV2::ColorRamp && gradientRamp && gradientRamp->type() == "gradient" )
689  {
690  //color ramp gradient
691  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( gradientRamp );
692  gradRamp->addStopsToGradient( &gradient, context.alpha() );
693  }
694  else
695  {
696  //two color gradient
697  gradient.setColorAt( 0.0, fillColor );
698  gradient.setColorAt( 1.0, fillColor2 );
699  }
700 
701  //update QBrush use gradient
702  brush = QBrush( gradient );
703 }
704 
706 {
707  QColor selColor = context.renderContext().selectionColor();
708  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
709  mSelBrush = QBrush( selColor );
710 
711  //update mBrush to use a gradient fill with specified properties
712  prepareExpressions( context.fields() );
713 }
714 
716 {
717  Q_UNUSED( context );
718 }
719 
720 void QgsGradientFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
721 {
722  QPainter* p = context.renderContext().painter();
723  if ( !p )
724  {
725  return;
726  }
727 
728  QPen mSelPen;
729  applyDataDefinedSymbology( context, points );
730 
731  p->setBrush( context.selected() ? mSelBrush : mBrush );
732  p->setPen( QPen( Qt::NoPen ) );
733 
734  QPointF offset;
735  if ( !mOffset.isNull() )
736  {
739  p->translate( offset );
740  }
741 
742  _renderPolygon( p, points, rings, context );
743 
744  if ( !mOffset.isNull() )
745  {
746  p->translate( -offset );
747  }
748 }
749 
751 {
752  QgsStringMap map;
753  map["gradient_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
754  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
755  map["color_type"] = QString::number( mGradientColorType );
756  map["type"] = QString::number( mGradientType );
757  map["coordinate_mode"] = QString::number( mCoordinateMode );
758  map["spread"] = QString::number( mGradientSpread );
759  map["reference_point1"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint1 );
760  map["reference_point1_iscentroid"] = QString::number( mReferencePoint1IsCentroid );
761  map["reference_point2"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint2 );
762  map["reference_point2_iscentroid"] = QString::number( mReferencePoint2IsCentroid );
763  map["angle"] = QString::number( mAngle );
764  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
767  if ( mGradientRamp )
768  {
769  map.unite( mGradientRamp->properties() );
770  }
771  return map;
772 }
773 
775 {
777  if ( mGradientRamp )
778  sl->setColorRamp( mGradientRamp->clone() );
783  sl->setAngle( mAngle );
784  sl->setOffset( mOffset );
785  sl->setOffsetUnit( mOffsetUnit );
787  return sl;
788 }
789 
791 {
792  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
793  return offsetBleed;
794 }
795 
796 //QgsShapeburstFillSymbolLayer
797 
799  int blurRadius, bool useWholeShape, double maxDistance ) :
800 
801  mBlurRadius( blurRadius ),
802  mUseWholeShape( useWholeShape ),
803  mMaxDistance( maxDistance ),
804  mDistanceUnit( QgsSymbolV2::MM ),
805  mColorType( colorType ),
806  mColor2( color2 ),
807  mGradientRamp( NULL ),
808  mTwoColorGradientRamp( 0 ),
809  mIgnoreRings( false ),
810  mOffsetUnit( QgsSymbolV2::MM )
811 {
812  mColor = color;
813 }
814 
816 {
817  delete mGradientRamp;
818 }
819 
821 {
822  //default to a two-color gradient
824  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
825  int blurRadius = 0;
826  bool useWholeShape = true;
827  double maxDistance = 5;
828  QPointF offset;
829 
830  //update fill properties from props
831  if ( props.contains( "color_type" ) )
832  {
833  colorType = ( ShapeburstColorType )props["color_type"].toInt();
834  }
835  if ( props.contains( "shapeburst_color" ) )
836  {
837  color = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color"] );
838  }
839  if ( props.contains( "shapeburst_color2" ) )
840  {
841  color2 = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color2"] );
842  }
843  if ( props.contains( "blur_radius" ) )
844  {
845  blurRadius = props["blur_radius"].toInt();
846  }
847  if ( props.contains( "use_whole_shape" ) )
848  {
849  useWholeShape = props["use_whole_shape"].toInt();
850  }
851  if ( props.contains( "max_distance" ) )
852  {
853  maxDistance = props["max_distance"].toDouble();
854  }
855  if ( props.contains( "offset" ) )
856  {
857  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
858  }
859 
860  //attempt to create color ramp from props
862 
863  //create a new shapeburst fill layer with desired properties
864  QgsShapeburstFillSymbolLayerV2* sl = new QgsShapeburstFillSymbolLayerV2( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
865  sl->setOffset( offset );
866  if ( props.contains( "offset_unit" ) )
867  {
868  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
869  }
870  if ( props.contains( "distance_unit" ) )
871  {
872  sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
873  }
874  if ( props.contains( "ignore_rings" ) )
875  {
876  sl->setIgnoreRings( props["ignore_rings"].toInt() );
877  }
878  if ( gradientRamp )
879  {
880  sl->setColorRamp( gradientRamp );
881  }
882 
883  if ( props.contains( "color_expression" ) )
884  sl->setDataDefinedProperty( "color", props["color_expression"] );
885  if ( props.contains( "color2_expression" ) )
886  sl->setDataDefinedProperty( "color2", props["color2_expression"] );
887  if ( props.contains( "blur_radius_expression" ) )
888  sl->setDataDefinedProperty( "blur_radius", props["blur_radius_expression"] );
889  if ( props.contains( "use_whole_shape_expression" ) )
890  sl->setDataDefinedProperty( "use_whole_shape", props["use_whole_shape_expression"] );
891  if ( props.contains( "max_distance_expression" ) )
892  sl->setDataDefinedProperty( "max_distance", props["max_distance_expression"] );
893  if ( props.contains( "ignore_rings_expression" ) )
894  sl->setDataDefinedProperty( "ignore_rings", props["ignore_rings_expression"] );
895 
896  return sl;
897 }
898 
900 {
901  return "ShapeburstFill";
902 }
903 
905 {
906  delete mGradientRamp;
907  mGradientRamp = ramp;
908 }
909 
910 void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
911  double& maxDistance, bool& ignoreRings )
912 {
913  //first gradient color
914  QgsExpression* colorExpression = expression( "color" );
915  color = mColor;
916  if ( colorExpression )
917  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
918 
919  //second gradient color
920  QgsExpression* colorExpression2 = expression( "color2" );
921  color2 = mColor2;
922  if ( colorExpression2 )
923  color2 = QgsSymbolLayerV2Utils::decodeColor( colorExpression2->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
924 
925  //blur radius
926  QgsExpression* blurRadiusExpression = expression( "blur_radius" );
927  blurRadius = mBlurRadius;
928  if ( blurRadiusExpression )
929  blurRadius = blurRadiusExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toInt();
930 
931  //use whole shape
932  QgsExpression* useWholeShapeExpression = expression( "use_whole_shape" );
933  useWholeShape = mUseWholeShape;
934  if ( useWholeShapeExpression )
935  useWholeShape = useWholeShapeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
936 
937  //max distance
938  QgsExpression* maxDistanceExpression = expression( "max_distance" );
939  maxDistance = mMaxDistance;
940  if ( maxDistanceExpression )
941  maxDistance = maxDistanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
942 
943  //ignore rings
944  QgsExpression* ignoreRingsExpression = expression( "ignore_rings" );
945  ignoreRings = mIgnoreRings;
946  if ( ignoreRingsExpression )
947  ignoreRings = ignoreRingsExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
948 
949 }
950 
952 {
953  //TODO - check this
954  QColor selColor = context.renderContext().selectionColor();
955  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
956  mSelBrush = QBrush( selColor );
957 
958  prepareExpressions( context.fields() );
959 }
960 
962 {
963  Q_UNUSED( context );
964 }
965 
966 void QgsShapeburstFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
967 {
968  QPainter* p = context.renderContext().painter();
969  if ( !p )
970  {
971  return;
972  }
973 
974  if ( context.selected() )
975  {
976  //feature is selected, draw using selection style
977  p->setBrush( mSelBrush );
978  QPointF offset;
979  if ( !mOffset.isNull() )
980  {
983  p->translate( offset );
984  }
985  _renderPolygon( p, points, rings, context );
986  if ( !mOffset.isNull() )
987  {
988  p->translate( -offset );
989  }
990  return;
991  }
992 
993  QColor color1, color2;
994  int blurRadius;
995  bool useWholeShape;
996  double maxDistance;
997  bool ignoreRings;
998  //calculate data defined symbology
999  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1000 
1001  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1002  int outputPixelMaxDist = 0;
1003  if ( !useWholeShape && maxDistance != 0 )
1004  {
1005  //convert max distance to pixels
1006  const QgsRenderContext& ctx = context.renderContext();
1007  outputPixelMaxDist = maxDistance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit );
1008  }
1009 
1010  //if we are using the two color mode, create a gradient ramp
1012  {
1013  mTwoColorGradientRamp = new QgsVectorGradientColorRampV2( color1, color2 );
1014  }
1015 
1016  //no border for shapeburst fills
1017  p->setPen( QPen( Qt::NoPen ) );
1018 
1019  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1020  int sideBuffer = 4 + ( blurRadius + 2 ) * 4 ;
1021  //create a QImage to draw shapeburst in
1022  double imWidth = points.boundingRect().width() + ( sideBuffer * 2 );
1023  double imHeight = points.boundingRect().height() + ( sideBuffer * 2 );
1024  QImage * fillImage = new QImage( imWidth * context.renderContext().rasterScaleFactor(),
1025  imHeight * context.renderContext().rasterScaleFactor(), QImage::Format_ARGB32_Premultiplied );
1026  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1027  //polygon boundary. Since we don't care about pixels which fall outside the polygon, we start with a black image and then draw over it the
1028  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1029  fillImage->fill( Qt::black );
1030 
1031  //also create an image to store the alpha channel
1032  QImage * alphaImage = new QImage( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1033  //initially fill the alpha channel image with a transparent color
1034  alphaImage->fill( Qt::transparent );
1035 
1036  //now, draw the polygon in the alpha channel image
1037  QPainter imgPainter;
1038  imgPainter.begin( alphaImage );
1039  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1040  imgPainter.setBrush( QBrush( Qt::white ) );
1041  imgPainter.setPen( QPen( Qt::black ) );
1042  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1043  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1044  _renderPolygon( &imgPainter, points, rings, context );
1045  imgPainter.end();
1046 
1047  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1048  //(this avoids calling _renderPolygon twice, since that can be slow)
1049  imgPainter.begin( fillImage );
1050  if ( !ignoreRings )
1051  {
1052  imgPainter.drawImage( 0, 0, *alphaImage );
1053  }
1054  else
1055  {
1056  //using ignore rings mode, so the alpha image can't be used
1057  //directly as the alpha channel contains polygon rings and we need
1058  //to draw now without any rings
1059  imgPainter.setBrush( QBrush( Qt::white ) );
1060  imgPainter.setPen( QPen( Qt::black ) );
1061  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1062  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1063  _renderPolygon( &imgPainter, points, NULL, context );
1064  }
1065  imgPainter.end();
1066 
1067  //apply distance transform to image, uses the current color ramp to calculate final pixel colours
1068  double * dtArray = distanceTransform( fillImage );
1069 
1070  //copy distance transform values back to QImage, shading by appropriate color ramp
1072  context.alpha(), useWholeShape, outputPixelMaxDist );
1073 
1074  //clean up some variables
1075  delete [] dtArray;
1077  {
1078  delete mTwoColorGradientRamp;
1079  }
1080 
1081  //apply blur if desired
1082  if ( blurRadius > 0 )
1083  {
1084  QgsSymbolLayerV2Utils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1085  }
1086 
1087  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1088  imgPainter.begin( fillImage );
1089  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1090  imgPainter.drawImage( 0, 0, *alphaImage );
1091  imgPainter.end();
1092  //we're finished with the alpha channel image now
1093  delete alphaImage;
1094 
1095  //draw shapeburst image in correct place in the destination painter
1096 
1097  p->save();
1098  QPointF offset;
1099  if ( !mOffset.isNull() )
1100  {
1103  p->translate( offset );
1104  }
1105 
1106  p->scale( 1 / context.renderContext().rasterScaleFactor(), 1 / context.renderContext().rasterScaleFactor() );
1107  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1108 
1109  delete fillImage;
1110 
1111  if ( !mOffset.isNull() )
1112  {
1113  p->translate( -offset );
1114  }
1115  p->restore();
1116 
1117 }
1118 
1119 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1120 
1121 /* distance transform of a 1d function using squared distance */
1122 void QgsShapeburstFillSymbolLayerV2::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1123 {
1124  int k = 0;
1125  v[0] = 0;
1126  z[0] = -INF;
1127  z[1] = + INF;
1128  for ( int q = 1; q <= n - 1; q++ )
1129  {
1130  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1131  while ( s <= z[k] )
1132  {
1133  k--;
1134  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1135  }
1136  k++;
1137  v[k] = q;
1138  z[k] = s;
1139  z[k+1] = + INF;
1140  }
1141 
1142  k = 0;
1143  for ( int q = 0; q <= n - 1; q++ )
1144  {
1145  while ( z[k+1] < q )
1146  k++;
1147  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1148  }
1149 }
1150 
1151 /* distance transform of 2d function using squared distance */
1152 void QgsShapeburstFillSymbolLayerV2::distanceTransform2d( double * im, int width, int height )
1153 {
1154  double *f = new double[ qMax( width,height )];
1155  int *v = new int[ qMax( width,height )];
1156  double *z = new double[ qMax( width,height ) + 1 ];
1157  double *d = new double[ qMax( width,height )];
1158 
1159  // transform along columns
1160  for ( int x = 0; x < width; x++ )
1161  {
1162  for ( int y = 0; y < height; y++ )
1163  {
1164  f[y] = im[ x + y * width ];
1165  }
1166  distanceTransform1d( f, height, v, z, d );
1167  for ( int y = 0; y < height; y++ )
1168  {
1169  im[ x + y * width ] = d[y];
1170  }
1171  }
1172 
1173  // transform along rows
1174  for ( int y = 0; y < height; y++ )
1175  {
1176  for ( int x = 0; x < width; x++ )
1177  {
1178  f[x] = im[ x + y*width ];
1179  }
1180  distanceTransform1d( f, width, v, z, d );
1181  for ( int x = 0; x < width; x++ )
1182  {
1183  im[ x + y*width ] = d[x];
1184  }
1185  }
1186 
1187  delete [] d;
1188  delete [] f;
1189  delete [] v;
1190  delete [] z;
1191 }
1192 
1193 /* distance transform of a binary QImage */
1195 {
1196  int width = im->width();
1197  int height = im->height();
1198 
1199  double * dtArray = new double[width * height];
1200 
1201  //load qImage to array
1202  QRgb tmpRgb;
1203  int idx = 0;
1204  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1205  {
1206  QRgb* scanLine = ( QRgb* )im->constScanLine( heightIndex );
1207  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1208  {
1209  tmpRgb = scanLine[widthIndex];
1210  if ( qRed( tmpRgb ) == 0 )
1211  {
1212  //black pixel, so zero distance
1213  dtArray[ idx ] = 0;
1214  }
1215  else
1216  {
1217  //white pixel, so initially set distance as infinite
1218  dtArray[ idx ] = INF;
1219  }
1220  idx++;
1221  }
1222  }
1223 
1224  //calculate squared distance transform
1225  distanceTransform2d( dtArray, width, height );
1226 
1227  return dtArray;
1228 }
1229 
1230 void QgsShapeburstFillSymbolLayerV2::dtArrayToQImage( double * array, QImage *im, QgsVectorColorRampV2* ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1231 {
1232  //find maximum distance value
1233  double maxDistanceValue;
1234 
1235  if ( useWholeShape )
1236  {
1237  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1238  double dtMaxValue = array[0];
1239  for ( int i = 1; i < ( im->width() * im->height() ); ++i )
1240  {
1241  dtMaxValue = qMax( dtMaxValue, array[i] );
1242  }
1243 
1244  //values in distance transform are squared
1245  maxDistanceValue = sqrt( dtMaxValue );
1246  }
1247  else
1248  {
1249  //use max distance set in symbol properties
1250  maxDistanceValue = maxPixelDistance;
1251  }
1252 
1253  //update the pixels in the provided QImage
1254  int idx = 0;
1255  double squaredVal = 0;
1256  double pixVal = 0;
1257  QColor pixColor;
1258 
1259  for ( int heightIndex = 0; heightIndex < im->height(); ++heightIndex )
1260  {
1261  QRgb* scanLine = ( QRgb* )im->scanLine( heightIndex );
1262  for ( int widthIndex = 0; widthIndex < im->width(); ++widthIndex )
1263  {
1264  //result of distance transform
1265  squaredVal = array[idx];
1266 
1267  //scale result to fit in the range [0, 1]
1268  pixVal = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1269 
1270  //convert value to color from ramp
1271  pixColor = ramp->color( pixVal );
1272 
1273  //apply layer's transparency to alpha value
1274  double alpha = pixColor.alpha() * layerAlpha;
1275 
1276  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1277  QgsSymbolLayerV2Utils::premultiplyColor( pixColor, alpha );
1278  scanLine[widthIndex] = pixColor.rgba();
1279  idx++;
1280  }
1281  }
1282 }
1283 
1285 {
1286  QgsStringMap map;
1287  map["shapeburst_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1288  map["shapeburst_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
1289  map["color_type"] = QString::number( mColorType );
1290  map["blur_radius"] = QString::number( mBlurRadius );
1291  map["use_whole_shape"] = QString::number( mUseWholeShape );
1292  map["max_distance"] = QString::number( mMaxDistance );
1293  map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1294  map["ignore_rings"] = QString::number( mIgnoreRings );
1295  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1296  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1297 
1299 
1300  if ( mGradientRamp )
1301  {
1302  map.unite( mGradientRamp->properties() );
1303  }
1304 
1305  return map;
1306 }
1307 
1309 {
1311  if ( mGradientRamp )
1312  {
1313  sl->setColorRamp( mGradientRamp->clone() );
1314  }
1317  sl->setOffset( mOffset );
1318  sl->setOffsetUnit( mOffsetUnit );
1320  return sl;
1321 }
1322 
1324 {
1325  double offsetBleed = qMax( mOffset.x(), mOffset.y() );
1326  return offsetBleed;
1327 }
1328 
1329 //QgsImageFillSymbolLayer
1330 
1331 QgsImageFillSymbolLayer::QgsImageFillSymbolLayer(): mOutlineWidth( 0.0 ), mOutlineWidthUnit( QgsSymbolV2::MM ), mOutline( 0 )
1332 {
1333  setSubSymbol( new QgsLineSymbolV2() );
1334 }
1335 
1337 {
1338 }
1339 
1340 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
1341 {
1342  QPainter* p = context.renderContext().painter();
1343  if ( !p )
1344  {
1345  return;
1346  }
1347 
1348  mNextAngle = mAngle;
1349  applyDataDefinedSettings( context );
1350 
1351  p->setPen( QPen( Qt::NoPen ) );
1352  if ( context.selected() )
1353  {
1354  QColor selColor = context.renderContext().selectionColor();
1355  // Alister - this doesn't seem to work here
1356  //if ( ! selectionIsOpaque )
1357  // selColor.setAlphaF( context.alpha() );
1358  p->setBrush( QBrush( selColor ) );
1359  _renderPolygon( p, points, rings, context );
1360  }
1361 
1362  if ( qgsDoubleNear( mNextAngle, 0.0 ) )
1363  {
1364  p->setBrush( mBrush );
1365  }
1366  else
1367  {
1368  QTransform t = mBrush.transform();
1369  t.rotate( mNextAngle );
1370  QBrush rotatedBrush = mBrush;
1371  rotatedBrush.setTransform( t );
1372  p->setBrush( rotatedBrush );
1373  }
1374  _renderPolygon( p, points, rings, context );
1375  if ( mOutline )
1376  {
1377  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1378  if ( rings )
1379  {
1380  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1381  for ( ; ringIt != rings->constEnd(); ++ringIt )
1382  {
1383  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1384  }
1385  }
1386  }
1387 }
1388 
1390 {
1391  if ( !symbol ) //unset current outline
1392  {
1393  delete mOutline;
1394  mOutline = 0;
1395  return true;
1396  }
1397 
1398  if ( symbol->type() != QgsSymbolV2::Line )
1399  {
1400  delete symbol;
1401  return false;
1402  }
1403 
1404  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1405  if ( lineSymbol )
1406  {
1407  delete mOutline;
1408  mOutline = lineSymbol;
1409  return true;
1410  }
1411 
1412  delete symbol;
1413  return false;
1414 }
1415 
1416 
1418 {
1419  if ( mOutline && mOutline->symbolLayer( 0 ) )
1420  {
1421  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
1422  return subLayerBleed;
1423  }
1424  return 0;
1425 }
1426 
1427 double QgsImageFillSymbolLayer::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
1428 {
1429  double width = mOutlineWidth;
1430  QgsExpression* widthExpression = expression( "width" );
1431  if ( widthExpression )
1432  {
1433  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1434  }
1435  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), mOutlineWidthUnit, e.mapUnits() );
1436 }
1437 
1439 {
1440  Q_UNUSED( context );
1441  if ( !mOutline )
1442  {
1443  return QColor( Qt::black );
1444  }
1445  return mOutline->color();
1446 }
1447 
1449 {
1450  return Qt::SolidLine;
1451 #if 0
1452  if ( !mOutline )
1453  {
1454  return Qt::SolidLine;
1455  }
1456  else
1457  {
1458  return mOutline->dxfPenStyle();
1459  }
1460 #endif //0
1461 }
1462 
1463 
1464 //QgsSVGFillSymbolLayer
1465 
1466 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
1467  mPatternWidthUnit( QgsSymbolV2::MM ), mSvgOutlineWidthUnit( QgsSymbolV2::MM )
1468 {
1469  setSvgFilePath( svgFilePath );
1470  mOutlineWidth = 0.3;
1471  mAngle = angle;
1473  mSvgPattern = 0;
1474 }
1475 
1476 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
1477  mSvgData( svgData )
1478 {
1479  storeViewBox();
1480  mOutlineWidth = 0.3;
1481  mAngle = angle;
1482  setSubSymbol( new QgsLineSymbolV2() );
1484  mSvgPattern = 0;
1485 }
1486 
1488 {
1489  delete mSvgPattern;
1490 }
1491 
1493 {
1494  mPatternWidthUnit = unit;
1495  mSvgOutlineWidthUnit = unit;
1496  mOutlineWidthUnit = unit;
1497 }
1498 
1500 {
1502  if ( mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
1503  {
1504  return QgsSymbolV2::Mixed;
1505  }
1506  return unit;
1507 }
1508 
1509 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
1510 {
1512  storeViewBox();
1513 
1514  mSvgFilePath = svgPath;
1516 }
1517 
1519 {
1520  QByteArray data;
1521  double width = 20;
1522  QString svgFilePath;
1523  double angle = 0.0;
1524 
1525  if ( properties.contains( "width" ) )
1526  {
1527  width = properties["width"].toDouble();
1528  }
1529  if ( properties.contains( "svgFile" ) )
1530  {
1531  QString svgName = properties["svgFile"];
1532  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
1533  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
1534  }
1535  if ( properties.contains( "angle" ) )
1536  {
1537  angle = properties["angle"].toDouble();
1538  }
1539 
1540  QgsSVGFillSymbolLayer* symbolLayer = 0;
1541  if ( !svgFilePath.isEmpty() )
1542  {
1543  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1544  }
1545  else
1546  {
1547  if ( properties.contains( "data" ) )
1548  {
1549  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1550  }
1551  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1552  }
1553 
1554  //svg parameters
1555  if ( properties.contains( "svgFillColor" ) )
1556  {
1557  symbolLayer->setSvgFillColor( QColor( properties["svgFillColor"] ) );
1558  }
1559  if ( properties.contains( "svgOutlineColor" ) )
1560  {
1561  symbolLayer->setSvgOutlineColor( QColor( properties["svgOutlineColor"] ) );
1562  }
1563  if ( properties.contains( "svgOutlineWidth" ) )
1564  {
1565  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1566  }
1567 
1568  //units
1569  if ( properties.contains( "pattern_width_unit" ) )
1570  {
1571  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1572  }
1573  if ( properties.contains( "svg_outline_width_unit" ) )
1574  {
1575  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1576  }
1577  if ( properties.contains( "outline_width_unit" ) )
1578  {
1579  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1580  }
1581 
1582  if ( properties.contains( "width_expression" ) )
1583  symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
1584  if ( properties.contains( "svgFile_expression" ) )
1585  symbolLayer->setDataDefinedProperty( "svgFile", properties["svgFile_expression"] );
1586  if ( properties.contains( "angle_expression" ) )
1587  symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
1588  if ( properties.contains( "svgFillColor_expression" ) )
1589  symbolLayer->setDataDefinedProperty( "svgFillColor", "svgFillColor_expression" );
1590  if ( properties.contains( "svgOutlineColor_expression" ) )
1591  symbolLayer->setDataDefinedProperty( "svgOutlineColor", "svgOutlineColor_expression" );
1592  if ( properties.contains( "svgOutlineWidth" ) )
1593  symbolLayer->setDataDefinedProperty( "svgOutlineWidth", "svgOutlineWidth_expression" );
1594 
1595  return symbolLayer;
1596 }
1597 
1599 {
1600  return "SVGFill";
1601 }
1602 
1603 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1604  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1605  QgsSymbolV2::OutputUnit svgOutlineWidthUnit, const QgsSymbolV2RenderContext& context )
1606 {
1607  if ( mSvgViewBox.isNull() )
1608  {
1609  return;
1610  }
1611 
1612  delete mSvgPattern;
1613  mSvgPattern = 0;
1615 
1616  if (( int )size < 1.0 || 10000.0 < size )
1617  {
1618  mSvgPattern = new QImage();
1619  brush.setTextureImage( *mSvgPattern );
1620  }
1621  else
1622  {
1623  bool fitsInCache = true;
1625  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1626  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1627  if ( !fitsInCache )
1628  {
1629  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1630  context.renderContext().scaleFactor(), 1.0 );
1631  double hwRatio = 1.0;
1632  if ( patternPict.width() > 0 )
1633  {
1634  hwRatio = ( double )patternPict.height() / ( double )patternPict.width();
1635  }
1636  mSvgPattern = new QImage(( int )size, ( int )( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1637  mSvgPattern->fill( 0 ); // transparent background
1638 
1639  QPainter p( mSvgPattern );
1640  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1641  }
1642 
1643  QTransform brushTransform;
1644  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1645  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1646  {
1647  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1648  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1649  brush.setTextureImage( transparentImage );
1650  }
1651  else
1652  {
1653  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1654  }
1655  brush.setTransform( brushTransform );
1656  }
1657 }
1658 
1660 {
1661 
1663 
1664  if ( mOutline )
1665  {
1666  mOutline->startRender( context.renderContext() );
1667  }
1668 
1669  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
1670 }
1671 
1673 {
1674  if ( mOutline )
1675  {
1676  mOutline->stopRender( context.renderContext() );
1677  }
1678 }
1679 
1681 {
1682  QgsStringMap map;
1683  if ( !mSvgFilePath.isEmpty() )
1684  {
1685  map.insert( "svgFile", QgsSymbolLayerV2Utils::symbolPathToName( mSvgFilePath ) );
1686  }
1687  else
1688  {
1689  map.insert( "data", QString( mSvgData.toHex() ) );
1690  }
1691 
1692  map.insert( "width", QString::number( mPatternWidth ) );
1693  map.insert( "angle", QString::number( mAngle ) );
1694 
1695  //svg parameters
1696  map.insert( "svgFillColor", mSvgFillColor.name() );
1697  map.insert( "svgOutlineColor", mSvgOutlineColor.name() );
1698  map.insert( "svgOutlineWidth", QString::number( mSvgOutlineWidth ) );
1699 
1700  //units
1701  map["pattern_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit );
1702  map["svg_outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit );
1703  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
1704 
1706  return map;
1707 }
1708 
1710 {
1711  QgsSVGFillSymbolLayer* clonedLayer = 0;
1712  if ( !mSvgFilePath.isEmpty() )
1713  {
1714  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
1715  clonedLayer->setSvgFillColor( mSvgFillColor );
1716  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
1717  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
1718  }
1719  else
1720  {
1721  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
1722  }
1723 
1724  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
1726  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
1727 
1728  if ( mOutline )
1729  {
1730  clonedLayer->setSubSymbol( mOutline->clone() );
1731  }
1732  copyDataDefinedProperties( clonedLayer );
1733  return clonedLayer;
1734 }
1735 
1736 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1737 {
1738  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
1739  if ( !props.value( "uom", "" ).isEmpty() )
1740  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1741  element.appendChild( symbolizerElem );
1742 
1743  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1744 
1745  QDomElement fillElem = doc.createElement( "se:Fill" );
1746  symbolizerElem.appendChild( fillElem );
1747 
1748  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1749  fillElem.appendChild( graphicFillElem );
1750 
1751  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1752  graphicFillElem.appendChild( graphicElem );
1753 
1754  if ( !mSvgFilePath.isEmpty() )
1755  {
1757  }
1758  else
1759  {
1760  // TODO: create svg from data
1761  // <se:InlineContent>
1762  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
1763  }
1764 
1765  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
1766  {
1767  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
1768  }
1769 
1770  // <Rotation>
1771  QString angleFunc;
1772  bool ok;
1773  double angle = props.value( "angle", "0" ).toDouble( &ok );
1774  if ( !ok )
1775  {
1776  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1777  }
1778  else if ( angle + mAngle != 0 )
1779  {
1780  angleFunc = QString::number( angle + mAngle );
1781  }
1782  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1783 
1784  if ( mOutline )
1785  {
1786  // the outline sub symbol should be stored within the Stroke element,
1787  // but it will be stored in a separated LineSymbolizer because it could
1788  // have more than one layer
1789  mOutline->toSld( doc, element, props );
1790  }
1791 }
1792 
1794 {
1795  QgsDebugMsg( "Entered." );
1796 
1797  QString path, mimeType;
1798  QColor fillColor, borderColor;
1799  Qt::PenStyle penStyle;
1800  double size, borderWidth;
1801 
1802  QDomElement fillElem = element.firstChildElement( "Fill" );
1803  if ( fillElem.isNull() )
1804  return NULL;
1805 
1806  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1807  if ( graphicFillElem.isNull() )
1808  return NULL;
1809 
1810  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1811  if ( graphicElem.isNull() )
1812  return NULL;
1813 
1814  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
1815  return NULL;
1816 
1817  if ( mimeType != "image/svg+xml" )
1818  return NULL;
1819 
1820  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
1821 
1822  double angle = 0.0;
1823  QString angleFunc;
1824  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1825  {
1826  bool ok;
1827  double d = angleFunc.toDouble( &ok );
1828  if ( ok )
1829  angle = d;
1830  }
1831 
1832  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
1833  sl->setSvgFillColor( fillColor );
1834  sl->setSvgOutlineColor( borderColor );
1835  sl->setSvgOutlineWidth( borderWidth );
1836 
1837  // try to get the outline
1838  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1839  if ( !strokeElem.isNull() )
1840  {
1842  if ( l )
1843  {
1844  QgsSymbolLayerV2List layers;
1845  layers.append( l );
1846  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
1847  }
1848  }
1849 
1850  return sl;
1851 }
1852 
1854 {
1855  QgsExpression* widthExpression = expression( "width" );
1856  QgsExpression* svgFileExpression = expression( "svgFile" );
1857  QgsExpression* fillColorExpression = expression( "svgFillColor" );
1858  QgsExpression* outlineColorExpression = expression( "svgOutlineColor" );
1859  QgsExpression* outlineWidthExpression = expression( "svgOutlineWidth" );
1860  QgsExpression* angleExpression = expression( "angle" );
1861  if ( !widthExpression && !svgFileExpression && !fillColorExpression && !outlineColorExpression && !outlineWidthExpression && !angleExpression )
1862  {
1863  return; //no data defined settings
1864  }
1865 
1866  if ( angleExpression )
1867  {
1868  mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1869  }
1870 
1871  double width = mPatternWidth;
1872  if ( widthExpression )
1873  {
1874  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1875  }
1876  QString svgFile = mSvgFilePath;
1877  if ( svgFileExpression )
1878  {
1879  svgFile = svgFileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
1880  }
1881  QColor svgFillColor = mSvgFillColor;
1882  if ( fillColorExpression )
1883  {
1884  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1885  }
1887  if ( outlineColorExpression )
1888  {
1889  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1890  }
1891  double outlineWidth = mSvgOutlineWidth;
1892  if ( outlineWidthExpression )
1893  {
1894  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1895  }
1896  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
1897  mSvgOutlineWidthUnit, context );
1898 
1899 }
1900 
1902 {
1903  if ( !mSvgData.isEmpty() )
1904  {
1905  QSvgRenderer r( mSvgData );
1906  if ( r.isValid() )
1907  {
1908  mSvgViewBox = r.viewBoxF();
1909  return;
1910  }
1911  }
1912 
1913  mSvgViewBox = QRectF();
1914  return;
1915 }
1916 
1918 {
1919  //default values
1920  mSvgFillColor = QColor( 0, 0, 0 );
1921  mSvgOutlineColor = QColor( 0, 0, 0 );
1922  mSvgOutlineWidth = 0.3;
1923 
1924  if ( mSvgFilePath.isEmpty() )
1925  {
1926  return;
1927  }
1928 
1929  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
1930  QColor defaultFillColor, defaultOutlineColor;
1931  double defaultOutlineWidth;
1932  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam,
1933  defaultOutlineWidth );
1934 
1935  if ( hasFillParam )
1936  {
1937  mSvgFillColor = defaultFillColor;
1938  }
1939  if ( hasOutlineParam )
1940  {
1941  mSvgOutlineColor = defaultOutlineColor;
1942  }
1943  if ( hasOutlineWidthParam )
1944  {
1945  mSvgOutlineWidth = defaultOutlineWidth;
1946  }
1947 }
1948 
1949 
1951  mOffsetUnit( QgsSymbolV2::MM ), mFillLineSymbol( 0 )
1952 {
1953  setSubSymbol( new QgsLineSymbolV2() );
1954  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
1955 }
1956 
1958 {
1959  mFillLineSymbol->setWidth( w );
1960  mLineWidth = w;
1961 }
1962 
1964 {
1965  mFillLineSymbol->setColor( c );
1966  mColor = c;
1967 }
1968 
1970 {
1971  delete mFillLineSymbol;
1972 }
1973 
1975 {
1976  if ( !symbol )
1977  {
1978  return false;
1979  }
1980 
1981  if ( symbol->type() == QgsSymbolV2::Line )
1982  {
1983  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1984  if ( lineSymbol )
1985  {
1986  delete mFillLineSymbol;
1987  mFillLineSymbol = lineSymbol;
1988 
1989  return true;
1990  }
1991  }
1992  delete symbol;
1993  return false;
1994 }
1995 
1997 {
1998  return mFillLineSymbol;
1999 }
2000 
2002 {
2003  return 0;
2004 }
2005 
2007 {
2008  mDistanceUnit = unit;
2009  mLineWidthUnit = unit;
2010  mOffsetUnit = unit;
2011 }
2012 
2014 {
2016  if ( mLineWidthUnit != unit || mOffsetUnit != unit )
2017  {
2018  return QgsSymbolV2::Mixed;
2019  }
2020  return unit;
2021 }
2022 
2024 {
2026 
2027  //default values
2028  double lineAngle = 45;
2029  double distance = 5;
2030  double lineWidth = 0.5;
2031  QColor color( Qt::black );
2032  double offset = 0.0;
2033 
2034  if ( properties.contains( "lineangle" ) )
2035  {
2036  lineAngle = properties["lineangle"].toDouble();
2037  }
2038  patternLayer->setLineAngle( lineAngle );
2039 
2040  if ( properties.contains( "distance" ) )
2041  {
2042  distance = properties["distance"].toDouble();
2043  }
2044  patternLayer->setDistance( distance );
2045 
2046  if ( properties.contains( "linewidth" ) )
2047  {
2048  lineWidth = properties["linewidth"].toDouble();
2049  }
2050  patternLayer->setLineWidth( lineWidth );
2051 
2052  if ( properties.contains( "color" ) )
2053  {
2054  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
2055  }
2056  patternLayer->setColor( color );
2057 
2058  if ( properties.contains( "offset" ) )
2059  {
2060  offset = properties["offset"].toDouble();
2061  }
2062  patternLayer->setOffset( offset );
2063 
2064 
2065  if ( properties.contains( "distance_unit" ) )
2066  {
2067  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
2068  }
2069  if ( properties.contains( "line_width_unit" ) )
2070  {
2071  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
2072  }
2073  if ( properties.contains( "offset_unit" ) )
2074  {
2075  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
2076  }
2077 
2078  //data defined properties
2079  if ( properties.contains( "lineangle_expression" ) )
2080  {
2081  patternLayer->setDataDefinedProperty( "lineangle", properties["lineangle_expression"] );
2082  }
2083  if ( properties.contains( "distance_expression" ) )
2084  {
2085  patternLayer->setDataDefinedProperty( "distance", properties["distance_expression"] );
2086  }
2087  if ( properties.contains( "linewidth_expression" ) )
2088  {
2089  patternLayer->setDataDefinedProperty( "linewidth", properties["linewidth_expression"] );
2090  }
2091  if ( properties.contains( "color_expression" ) )
2092  {
2093  patternLayer->setDataDefinedProperty( "color", properties["color_expression"] );
2094  }
2095  return patternLayer;
2096 }
2097 
2099 {
2100  return "LinePatternFill";
2101 }
2102 
2103 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
2104  double lineWidth, const QColor& color )
2105 {
2106  Q_UNUSED( lineWidth );
2107  Q_UNUSED( color );
2108 
2109  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2110 
2111  if ( !mFillLineSymbol )
2112  {
2113  return;
2114  }
2115  // We have to make a copy because marker intervals will have to be adjusted
2116  QgsLineSymbolV2* fillLineSymbol = dynamic_cast<QgsLineSymbolV2*>( mFillLineSymbol->clone() );
2117  if ( !fillLineSymbol )
2118  {
2119  return;
2120  }
2121 
2122  const QgsRenderContext& ctx = context.renderContext();
2123  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit );
2124  double outputPixelDist = distance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit );
2125  double outputPixelOffset = mOffset * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mOffsetUnit );
2126 
2127  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2128  // For marker lines we have to get markers interval.
2129  double outputPixelBleed = 0;
2130  double outputPixelInterval = 0; // maximum interval
2131  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2132  {
2133  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2134  double layerBleed = layer->estimateMaxBleed();
2135  // TODO: to get real bleed we have to scale it using context and units,
2136  // unfortunately estimateMaxBleed() ignore units completely, e.g.
2137  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
2138  // offset regardless units. This has to be fixed especially
2139  // in estimateMaxBleed(), context probably has to be used.
2140  // For now, we only support millimeters
2141  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
2142  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
2143 
2144  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2145  if ( markerLineLayer )
2146  {
2147  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit() );
2148 
2149  // There may be multiple marker lines with different intervals.
2150  // In theory we should find the least common multiple, but that could be too
2151  // big (multiplication of intervals in the worst case).
2152  // Because patterns without small common interval would look strange, we
2153  // believe that the longest interval should usually be sufficient.
2154  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
2155  }
2156  }
2157 
2158  if ( outputPixelInterval > 0 )
2159  {
2160  // We have to adjust marker intervals to integer pixel size to get
2161  // repeatable pattern.
2162  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
2163  outputPixelInterval = qRound( outputPixelInterval );
2164 
2165  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2166  {
2167  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2168 
2169  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2170  if ( markerLineLayer )
2171  {
2172  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2173  }
2174  }
2175  }
2176 
2177  //create image
2178  int height, width;
2179  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2180  {
2181  height = outputPixelDist;
2182  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2183  }
2184  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2185  {
2186  width = outputPixelDist;
2187  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2188  }
2189  else
2190  {
2191  height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2192  width = outputPixelDist / sin( lineAngle * M_PI / 180 );
2193 
2194  // recalculate real angle and distance after rounding to pixels
2195  lineAngle = 180 * atan2(( double ) height, ( double ) width ) / M_PI;
2196  if ( lineAngle < 0 )
2197  {
2198  lineAngle += 360.;
2199  }
2200 
2201  height = qAbs( height );
2202  width = qAbs( width );
2203 
2204  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
2205 
2206  // Round offset to correspond to one pixel height, otherwise lines may
2207  // be shifted on tile border if offset falls close to pixel center
2208  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
2209  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
2210  }
2211 
2212  //depending on the angle, we might need to render into a larger image and use a subset of it
2213  double dx = 0;
2214  double dy = 0;
2215 
2216  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2217  // thus we add integer multiplications of width and heigh covering the bleed
2218  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
2219 
2220  // Always buffer at least once so that center of line marker in upper right corner
2221  // does not fall outside due to representation error
2222  bufferMulti = qMax( bufferMulti, 1 );
2223 
2224  int xBuffer = width * bufferMulti;
2225  int yBuffer = height * bufferMulti;
2226  int innerWidth = width;
2227  int innerHeight = height;
2228  width += 2 * xBuffer;
2229  height += 2 * yBuffer;
2230 
2231  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2232  {
2233  return;
2234  }
2235 
2236  QImage patternImage( width, height, QImage::Format_ARGB32 );
2237  patternImage.fill( 0 );
2238 
2239  QPointF p1, p2, p3, p4, p5, p6;
2240  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2241  {
2242  p1 = QPointF( 0, yBuffer );
2243  p2 = QPointF( width, yBuffer );
2244  p3 = QPointF( 0, yBuffer + innerHeight );
2245  p4 = QPointF( width, yBuffer + innerHeight );
2246  }
2247  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2248  {
2249  p1 = QPointF( xBuffer, height );
2250  p2 = QPointF( xBuffer, 0 );
2251  p3 = QPointF( xBuffer + innerWidth, height );
2252  p4 = QPointF( xBuffer + innerWidth, 0 );
2253  }
2254  else if ( lineAngle > 0 && lineAngle < 90 )
2255  {
2256  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2257  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2258  p1 = QPointF( 0, height );
2259  p2 = QPointF( width, 0 );
2260  p3 = QPointF( -dx, height - dy );
2261  p4 = QPointF( width - dx, -dy );
2262  p5 = QPointF( dx, height + dy );
2263  p6 = QPointF( width + dx, dy );
2264  }
2265  else if ( lineAngle > 180 && lineAngle < 270 )
2266  {
2267  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2268  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2269  p1 = QPointF( width, 0 );
2270  p2 = QPointF( 0, height );
2271  p3 = QPointF( width - dx, -dy );
2272  p4 = QPointF( -dx, height - dy );
2273  p5 = QPointF( width + dx, dy );
2274  p6 = QPointF( dx, height + dy );
2275  }
2276  else if ( lineAngle > 90 && lineAngle < 180 )
2277  {
2278  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2279  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2280  p1 = QPointF( 0, 0 );
2281  p2 = QPointF( width, height );
2282  p5 = QPointF( dx, -dy );
2283  p6 = QPointF( width + dx, height - dy );
2284  p3 = QPointF( -dx, dy );
2285  p4 = QPointF( width - dx, height + dy );
2286  }
2287  else if ( lineAngle > 270 && lineAngle < 360 )
2288  {
2289  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2290  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2291  p1 = QPointF( width, height );
2292  p2 = QPointF( 0, 0 );
2293  p5 = QPointF( width + dx, height - dy );
2294  p6 = QPointF( dx, -dy );
2295  p3 = QPointF( width - dx, height + dy );
2296  p4 = QPointF( -dx, dy );
2297  }
2298 
2299  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2300  {
2301  QPointF tempPt;
2302  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2303  p3 = QPointF( tempPt.x(), tempPt.y() );
2304  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2305  p4 = QPointF( tempPt.x(), tempPt.y() );
2306  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2307  p5 = QPointF( tempPt.x(), tempPt.y() );
2308  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2309  p6 = QPointF( tempPt.x(), tempPt.y() );
2310 
2311  //update p1, p2 last
2312  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2313  p1 = QPointF( tempPt.x(), tempPt.y() );
2314  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2315  p2 = QPointF( tempPt.x(), tempPt.y() );;
2316  }
2317 
2318  QPainter p( &patternImage );
2319 
2320 #if 0
2321  // DEBUG: Draw rectangle
2322  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2323  QPen pen( QColor( Qt::black ) );
2324  pen.setWidthF( 0.1 );
2325  pen.setCapStyle( Qt::FlatCap );
2326  p.setPen( pen );
2327 
2328  // To see this rectangle, comment buffer cut below.
2329  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2330  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 ) ;
2331  p.drawPolygon( polygon );
2332 
2333  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 ) ;
2334  p.drawPolygon( polygon );
2335 #endif
2336 
2337  // Use antialiasing because without antialiasing lines are rendered to the
2338  // right and below the mathematically defined points (not symetrical)
2339  // and such tiles become useless for are filling
2340  p.setRenderHint( QPainter::Antialiasing, true );
2341 
2342  // line rendering needs context for drawing on patternImage
2343  QgsRenderContext lineRenderContext;
2344  lineRenderContext.setPainter( &p );
2345  lineRenderContext.setRasterScaleFactor( 1.0 );
2346  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2348  lineRenderContext.setMapToPixel( mtp );
2349  lineRenderContext.setForceVectorOutput( false );
2350 
2351  fillLineSymbol->startRender( lineRenderContext );
2352 
2353  QVector<QPolygonF> polygons;
2354  polygons.append( QPolygonF() << p1 << p2 );
2355  polygons.append( QPolygonF() << p3 << p4 );
2356  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2357  {
2358  polygons.append( QPolygonF() << p5 << p6 );
2359  }
2360 
2361  foreach ( QPolygonF polygon, polygons )
2362  {
2363  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2364  }
2365 
2366  fillLineSymbol->stopRender( lineRenderContext );
2367  p.end();
2368 
2369  // Cut off the buffer
2370  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2371 
2372  //set image to mBrush
2373  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2374  {
2375  QImage transparentImage = patternImage.copy();
2376  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2377  brush.setTextureImage( transparentImage );
2378  }
2379  else
2380  {
2381  brush.setTextureImage( patternImage );
2382  }
2383 
2384  QTransform brushTransform;
2385  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2386  brush.setTransform( brushTransform );
2387 
2388  delete fillLineSymbol;
2389 }
2390 
2392 {
2394 
2395  if ( mFillLineSymbol )
2396  {
2398  }
2399 
2400  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
2401 }
2402 
2404 {
2405 }
2406 
2408 {
2409  QgsStringMap map;
2410  map.insert( "lineangle", QString::number( mLineAngle ) );
2411  map.insert( "distance", QString::number( mDistance ) );
2412  map.insert( "linewidth", QString::number( mLineWidth ) );
2413  map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mColor ) );
2414  map.insert( "offset", QString::number( mOffset ) );
2415  map.insert( "distance_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit ) );
2416  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
2417  map.insert( "offset_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ) );
2419  return map;
2420 }
2421 
2423 {
2425  if ( mFillLineSymbol )
2426  {
2427  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2428  }
2429  clonedLayer->setDistanceUnit( mDistanceUnit );
2430  clonedLayer->setLineWidthUnit( mLineWidthUnit );
2431  clonedLayer->setOffsetUnit( mOffsetUnit );
2432  copyDataDefinedProperties( clonedLayer );
2433  return clonedLayer;
2434 }
2435 
2436 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2437 {
2438  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2439  if ( !props.value( "uom", "" ).isEmpty() )
2440  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2441  element.appendChild( symbolizerElem );
2442 
2443  // <Geometry>
2444  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2445 
2446  QDomElement fillElem = doc.createElement( "se:Fill" );
2447  symbolizerElem.appendChild( fillElem );
2448 
2449  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2450  fillElem.appendChild( graphicFillElem );
2451 
2452  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2453  graphicFillElem.appendChild( graphicElem );
2454 
2455  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), mColor, Qt::SolidLine, mLineWidth, mDistance );
2456 
2457  // <Rotation>
2458  QString angleFunc;
2459  bool ok;
2460  double angle = props.value( "angle", "0" ).toDouble( &ok );
2461  if ( !ok )
2462  {
2463  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
2464  }
2465  else if ( angle + mLineAngle != 0 )
2466  {
2467  angleFunc = QString::number( angle + mLineAngle );
2468  }
2469  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2470 
2471  // <se:Displacement>
2472  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2473  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
2474 
2475  if ( mFillLineSymbol )
2476  {
2477  mFillLineSymbol->toSld( doc, element, props );
2478  }
2479 }
2480 
2482 {
2483  QString featureStyle;
2484  featureStyle.append( "Brush(" );
2485  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
2486  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
2487  featureStyle.append( ",id:\"ogr-brush-2\"" );
2488  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
2489  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2490  featureStyle.append( ",dx:0mm" );
2491  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2492  featureStyle.append( ")" );
2493  return featureStyle;
2494 }
2495 
2497 {
2498  QgsExpression* lineAngleExpression = expression( "lineangle" );
2499  QgsExpression* distanceExpression = expression( "distance" );
2500  QgsExpression* lineWidthExpression = expression( "linewidth" );
2501  QgsExpression* colorExpression = expression( "color" );
2502  if ( !lineAngleExpression && !distanceExpression && !lineWidthExpression && !colorExpression )
2503  {
2504  return; //no data defined settings
2505  }
2506 
2507  double lineAngle = mLineAngle;
2508  if ( lineAngleExpression )
2509  {
2510  lineAngle = lineAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2511  }
2512  double distance = mDistance;
2513  if ( distanceExpression )
2514  {
2515  distance = distanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2516  }
2517  double lineWidth = mLineWidth;
2518  if ( lineWidthExpression )
2519  {
2520  lineWidth = lineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2521  }
2522  QColor color = mColor;
2523  if ( colorExpression )
2524  {
2525  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
2526  }
2527  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
2528 }
2529 
2531 {
2532  QgsDebugMsg( "Entered." );
2533 
2534  QString name;
2535  QColor fillColor, lineColor;
2536  double size, lineWidth;
2537  Qt::PenStyle lineStyle;
2538 
2539  QDomElement fillElem = element.firstChildElement( "Fill" );
2540  if ( fillElem.isNull() )
2541  return NULL;
2542 
2543  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2544  if ( graphicFillElem.isNull() )
2545  return NULL;
2546 
2547  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2548  if ( graphicElem.isNull() )
2549  return NULL;
2550 
2551  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2552  return NULL;
2553 
2554  if ( name != "horline" )
2555  return NULL;
2556 
2557  double angle = 0.0;
2558  QString angleFunc;
2559  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2560  {
2561  bool ok;
2562  double d = angleFunc.toDouble( &ok );
2563  if ( ok )
2564  angle = d;
2565  }
2566 
2567  double offset = 0.0;
2568  QPointF vectOffset;
2569  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
2570  {
2571  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
2572  }
2573 
2575  sl->setColor( lineColor );
2576  sl->setLineWidth( lineWidth );
2577  sl->setLineAngle( angle );
2578  sl->setOffset( offset );
2579  sl->setDistance( size );
2580 
2581  // try to get the outline
2582  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2583  if ( !strokeElem.isNull() )
2584  {
2586  if ( l )
2587  {
2588  QgsSymbolLayerV2List layers;
2589  layers.append( l );
2590  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2591  }
2592  }
2593 
2594  return sl;
2595 }
2596 
2597 
2599 
2601  mDistanceXUnit( QgsSymbolV2::MM ), mDistanceY( 15 ), mDistanceYUnit( QgsSymbolV2::MM ), mDisplacementX( 0 ), mDisplacementXUnit( QgsSymbolV2::MM ),
2602  mDisplacementY( 0 ), mDisplacementYUnit( QgsSymbolV2::MM )
2603 {
2604  mDistanceX = 15;
2605  mDistanceY = 15;
2606  mDisplacementX = 0;
2607  mDisplacementY = 0;
2609  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
2610 }
2611 
2613 {
2614 }
2615 
2617 {
2618  mDistanceXUnit = unit;
2619  mDistanceYUnit = unit;
2620  mDisplacementXUnit = unit;
2621  mDisplacementYUnit = unit;
2622 }
2623 
2625 {
2627  if ( mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
2628  {
2629  return QgsSymbolV2::Mixed;
2630  }
2631  return unit;
2632 }
2633 
2635 {
2637  if ( properties.contains( "distance_x" ) )
2638  {
2639  layer->setDistanceX( properties["distance_x"].toDouble() );
2640  }
2641  if ( properties.contains( "distance_y" ) )
2642  {
2643  layer->setDistanceY( properties["distance_y"].toDouble() );
2644  }
2645  if ( properties.contains( "displacement_x" ) )
2646  {
2647  layer->setDisplacementX( properties["displacement_x"].toDouble() );
2648  }
2649  if ( properties.contains( "displacement_y" ) )
2650  {
2651  layer->setDisplacementY( properties["displacement_y"].toDouble() );
2652  }
2653 
2654  if ( properties.contains( "distance_x_unit" ) )
2655  {
2656  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
2657  }
2658  if ( properties.contains( "distance_y_unit" ) )
2659  {
2660  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
2661  }
2662  if ( properties.contains( "displacement_x_unit" ) )
2663  {
2664  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
2665  }
2666  if ( properties.contains( "displacement_y_unit" ) )
2667  {
2668  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
2669  }
2670 
2671  //data defined properties
2672  if ( properties.contains( "distance_x_expression" ) )
2673  {
2674  layer->setDataDefinedProperty( "distance_x", properties["distance_x_expression"] );
2675  }
2676  if ( properties.contains( "distance_y_expression" ) )
2677  {
2678  layer->setDataDefinedProperty( "distance_y", properties["distance_y_expression"] );
2679  }
2680  if ( properties.contains( "displacement_x_expression" ) )
2681  {
2682  layer->setDataDefinedProperty( "displacement_x", properties["displacement_x_expression"] );
2683  }
2684  if ( properties.contains( "displacement_y_expression" ) )
2685  {
2686  layer->setDataDefinedProperty( "displacement_y", properties["displacement_y_expression"] );
2687  }
2688  return layer;
2689 }
2690 
2692 {
2693  return "PointPatternFill";
2694 }
2695 
2696 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
2697  double displacementX, double displacementY )
2698 {
2699  //render 3 rows and columns in one go to easily incorporate displacement
2700  const QgsRenderContext& ctx = context.renderContext();
2701  double width = distanceX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceXUnit ) * 2.0;
2702  double height = distanceY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceYUnit ) * 2.0;
2703 
2704  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2705  {
2706  QImage img;
2707  brush.setTextureImage( img );
2708  return;
2709  }
2710 
2711  QImage patternImage( width, height, QImage::Format_ARGB32 );
2712  patternImage.fill( 0 );
2713 
2714  if ( mMarkerSymbol )
2715  {
2716  QPainter p( &patternImage );
2717 
2718  //marker rendering needs context for drawing on patternImage
2719  QgsRenderContext pointRenderContext;
2720  pointRenderContext.setPainter( &p );
2721  pointRenderContext.setRasterScaleFactor( 1.0 );
2722  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2724  pointRenderContext.setMapToPixel( mtp );
2725  pointRenderContext.setForceVectorOutput( false );
2726 
2727  mMarkerSymbol->startRender( pointRenderContext );
2728 
2729  //render corner points
2730  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
2731  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
2732  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
2733  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
2734 
2735  //render displaced points
2736  double displacementPixelX = displacementX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementXUnit );
2737  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit );
2738  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
2739  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
2740  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
2741  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
2742  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
2743 
2744  mMarkerSymbol->stopRender( pointRenderContext );
2745  }
2746 
2747  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2748  {
2749  QImage transparentImage = patternImage.copy();
2750  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2751  brush.setTextureImage( transparentImage );
2752  }
2753  else
2754  {
2755  brush.setTextureImage( patternImage );
2756  }
2757  QTransform brushTransform;
2758  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2759  brush.setTransform( brushTransform );
2760 }
2761 
2763 {
2765 
2766  if ( mOutline )
2767  {
2768  mOutline->startRender( context.renderContext() );
2769  }
2770  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
2771 }
2772 
2774 {
2775  if ( mOutline )
2776  {
2777  mOutline->stopRender( context.renderContext() );
2778  }
2779 }
2780 
2782 {
2783  QgsStringMap propertyMap;
2784  propertyMap["distance_x"] = QString::number( mDistanceX );
2785  propertyMap["distance_y"] = QString::number( mDistanceY );
2786  propertyMap["displacement_x"] = QString::number( mDisplacementX );
2787  propertyMap["displacement_y"] = QString::number( mDisplacementY );
2788  propertyMap["distance_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit );
2789  propertyMap["distance_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit );
2790  propertyMap["displacement_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit );
2791  propertyMap["displacement_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit );
2792  saveDataDefinedProperties( propertyMap );
2793  return propertyMap;
2794 }
2795 
2797 {
2799  if ( mMarkerSymbol )
2800  {
2801  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
2802  }
2803  copyDataDefinedProperties( clonedLayer );
2804  return clonedLayer;
2805 }
2806 
2807 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2808 {
2809  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
2810  {
2811  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2812  if ( !props.value( "uom", "" ).isEmpty() )
2813  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2814  element.appendChild( symbolizerElem );
2815 
2816  // <Geometry>
2817  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2818 
2819  QDomElement fillElem = doc.createElement( "se:Fill" );
2820  symbolizerElem.appendChild( fillElem );
2821 
2822  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2823  fillElem.appendChild( graphicFillElem );
2824 
2825  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
2826  QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
2827  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
2828  symbolizerElem.appendChild( distanceElem );
2829 
2831  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
2832  if ( !markerLayer )
2833  {
2834  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
2835  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
2836  }
2837  else
2838  {
2839  markerLayer->writeSldMarker( doc, graphicFillElem, props );
2840  }
2841  }
2842 }
2843 
2845 {
2846  Q_UNUSED( element );
2847  return NULL;
2848 }
2849 
2851 {
2852  if ( !symbol )
2853  {
2854  return false;
2855  }
2856 
2857  if ( symbol->type() == QgsSymbolV2::Marker )
2858  {
2859  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
2860  delete mMarkerSymbol;
2861  mMarkerSymbol = markerSymbol;
2862  }
2863  return true;
2864 }
2865 
2867 {
2868  QgsExpression* distanceXExpression = expression( "distance_x" );
2869  QgsExpression* distanceYExpression = expression( "distance_y" );
2870  QgsExpression* displacementXExpression = expression( "displacement_x" );
2871  QgsExpression* displacementYExpression = expression( "displacement_y" );
2872 
2873 #if 0
2874  // TODO: enable but check also if mMarkerSymbol has data defined properties
2875  if ( !distanceXExpression && !distanceYExpression && !displacementXExpression && !displacementYExpression )
2876  {
2877  return;
2878  }
2879 #endif
2880 
2881  double distanceX = mDistanceX;
2882  if ( distanceXExpression )
2883  {
2884  distanceX = distanceXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2885  }
2886  double distanceY = mDistanceY;
2887  if ( distanceYExpression )
2888  {
2889  distanceY = distanceYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2890  }
2891  double displacementX = mDisplacementX;
2892  if ( displacementXExpression )
2893  {
2894  displacementX = displacementXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2895  }
2896  double displacementY = mDisplacementY;
2897  if ( displacementYExpression )
2898  {
2899  displacementY = displacementYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2900  }
2901  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
2902 }
2903 
2905 {
2906  return 0;
2907 }
2908 
2910 {
2911  QSet<QString> attributes = QgsSymbolLayerV2::usedAttributes();
2912 
2913  if ( mMarkerSymbol )
2914  attributes.unite( mMarkerSymbol->usedAttributes() );
2915 
2916  return attributes;
2917 }
2918 
2920 
2921 
2923 {
2925 }
2926 
2928 {
2929  delete mMarker;
2930 }
2931 
2933 {
2934  Q_UNUSED( properties );
2935  return new QgsCentroidFillSymbolLayerV2();
2936 }
2937 
2939 {
2940  return "CentroidFill";
2941 }
2942 
2943 void QgsCentroidFillSymbolLayerV2::setColor( const QColor& color )
2944 {
2945  mMarker->setColor( color );
2946  mColor = color;
2947 }
2948 
2950 {
2951  mMarker->setAlpha( context.alpha() );
2952  mMarker->startRender( context.renderContext() );
2953 }
2954 
2956 {
2957  mMarker->stopRender( context.renderContext() );
2958 }
2959 
2960 void QgsCentroidFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
2961 {
2962  Q_UNUSED( rings );
2963 
2964  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
2965  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
2966 }
2967 
2969 {
2970  return QgsStringMap();
2971 }
2972 
2974 {
2976  x->setSubSymbol( mMarker->clone() );
2977  return x;
2978 }
2979 
2980 void QgsCentroidFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2981 {
2982  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
2983  // used with PointSymbolizer, then the semantic is to use the centroid
2984  // of the geometry, or any similar representative point.
2985  mMarker->toSld( doc, element, props );
2986 }
2987 
2989 {
2990  QgsDebugMsg( "Entered." );
2991 
2993  if ( !l )
2994  return NULL;
2995 
2996  QgsSymbolLayerV2List layers;
2997  layers.append( l );
2998  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
2999 
3001  x->setSubSymbol( marker );
3002  return x;
3003 }
3004 
3005 
3007 {
3008  return mMarker;
3009 }
3010 
3012 {
3013  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
3014  {
3015  delete symbol;
3016  return false;
3017  }
3018 
3019  delete mMarker;
3020  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
3021  mColor = mMarker->color();
3022  return true;
3023 }
3024 
3026 {
3027  QSet<QString> attributes;
3028 
3029  attributes.unite( QgsSymbolLayerV2::usedAttributes() );
3030 
3031  if ( mMarker )
3032  attributes.unite( mMarker->usedAttributes() );
3033 
3034  return attributes;
3035 }
3036 
3038 {
3039  if ( mMarker )
3040  {
3041  return mMarker->outputUnit();
3042  }
3043  return QgsSymbolV2::Mixed; //mOutputUnit;
3044 }
virtual QSet< QString > usedAttributes() const
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
static Qt::BrushStyle decodeBrushStyle(QString str)
void setForceVectorOutput(bool force)
Added in QGIS v1.5.
QgsSymbolV2::OutputUnit intervalUnit() const
QgsSymbolV2::OutputUnit patternWidthUnit() const
void setDistanceUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit for the maximum distance to shade inside of the shape from the polygon's boundary...
#define DEFAULT_SIMPLEFILL_BORDERCOLOR
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
double outlineWidth
Definition: qgssvgcache.cpp:78
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setReferencePoint1(QPointF referencePoint)
Starting point of gradient fill, in the range [0,0] - [1,1].
QgsSymbolV2::OutputUnit mSvgOutlineWidthUnit
void setReferencePoint2IsCentroid(bool isCentroid)
Sets the end point of the gradient to be the feature centroid.
void setSvgOutlineWidth(double w)
static void multiplyImageOpacity(QImage *image, qreal alpha)
Multiplies opacity of image pixel values with a (global) transparency value.
QgsStringMap properties() const
void stopRender(QgsSymbolV2RenderContext &context)
void startRender(QgsSymbolV2RenderContext &context)
void setAngle(double angle)
Rotation angle for gradient fill.
QString ogrFeatureStyleWidth(double widthScaleFactor) const
void applyDataDefinedSettings(const QgsSymbolV2RenderContext &context)
double ignoreRings() const
Returns whether the shapeburst fill is set to ignore polygon interior rings.
QgsSymbolV2::OutputUnit mLineWidthUnit
virtual QString type() const =0
static Q_DECL_DEPRECATED bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &borderColor, double &borderWidth, double &size)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
virtual Qt::PenStyle dxfPenStyle() const
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
void setSvgFillColor(const QColor &c)
QgsSymbolV2::OutputUnit outputUnit() const
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setDistanceYUnit(QgsSymbolV2::OutputUnit unit)
QString svgFilePath() const
virtual void applyDataDefinedSettings(const QgsSymbolV2RenderContext &context)
SymbolType type() const
Definition: qgssymbolv2.h:78
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
const QPicture & svgAsPicture(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool forceVectorOutput=false)
Get SVG as QPicture&.
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
QColor selectionColor() const
Added in QGIS v2.0.
QSet< QString > usedAttributes() const
virtual QSet< QString > usedAttributes() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const
bool setSubSymbol(QgsSymbolV2 *symbol)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
Base class for polygon renderers generating texture images.
GradientCoordinateMode mCoordinateMode
static void createRotationElement(QDomDocument &doc, QDomElement &element, QString rotationFunc)
double rendererScale() const
QgsSymbolV2::OutputUnit outputUnit() const
QgsSymbolV2::OutputUnit svgOutlineWidthUnit() const
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
void applyDataDefinedSymbology(QgsSymbolV2RenderContext &context, QColor &color, QColor &color2, int &blurRadius, bool &useWholeShape, double &maxDistance, bool &ignoreRings)
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, QColor color=QColor())
QgsSymbolLayerV2 * clone() const
void distanceTransform1d(double *f, int n, int *v, double *z, double *d)
static QPointF decodePoint(QString str)
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
QgsSymbolV2::OutputUnit outputUnit() const
virtual QgsStringMap properties() const =0
static QDomElement createVendorOptionElement(QDomDocument &doc, QString name, QString value)
GradientCoordinateMode coordinateMode() const
Coordinate mode for gradient.
static QColor decodeColor(QString str)
QgsSymbolV2::OutputUnit mDisplacementXUnit
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
static const bool selectionIsOpaque
int blurRadius() const
Returns the blur radius, which controls the amount of blurring applied to the fill.
double useWholeShape() const
Returns whether the shapeburst fill is set to cover the entire shape.
double scaleFactor() const
void stopRender(QgsSymbolV2RenderContext &context)
virtual QColor dxfColor(const QgsSymbolV2RenderContext &context) const
double mDistance
Distance (in mm or map units) between lines.
QColor fillColor() const
Get fill color.
QgsSymbolLayerV2 * clone() const
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setSvgOutlineWidthUnit(QgsSymbolV2::OutputUnit unit)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
double mLineAngle
Vector line angle in degrees (0 = horizontal, counterclockwise)
virtual QColor fillColor() const
Get fill color.
void applyDataDefinedSymbology(QgsSymbolV2RenderContext &context, const QPolygonF &points)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
QgsVectorColorRampV2 * mGradientRamp
QMap< QString, QString > QgsStringMap
Definition: qgis.h:416
void startRender(QgsSymbolV2RenderContext &context)
QgsSymbolLayerV2 * clone() const
void setColorRamp(QgsVectorColorRampV2 *ramp)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:324
QgsSymbolV2::OutputUnit outputUnit() const
void setWidth(double width)
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setSvgOutlineColor(const QColor &c)
static QString encodeColor(QColor color)
void setInterval(double interval)
void applyPattern(const QgsSymbolV2RenderContext &context, QBrush &brush, double lineAngle, double distance, double lineWidth, const QColor &color)
Applies the svg pattern to the brush.
QgsSymbolV2::OutputUnit outputUnit() const
void setDistanceUnit(QgsSymbolV2::OutputUnit unit)
virtual QgsExpression * expression(const QString &property) const
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasOutlineParam, QColor &defaultOutlineColor, bool &hasOutlineWidthParam, double &defaultOutlineWidth) const
Tests if an svg file contains parameters for fill, outline color, outline width.
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:172
static QgsSvgCache * instance()
Definition: qgssvgcache.cpp:84
static QString encodePenStyle(Qt::PenStyle style)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
bool setSubSymbol(QgsSymbolV2 *symbol)
const QImage & svgAsImage(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool &fitsInCache)
Get SVG as QImage.
QgsSymbolV2::OutputUnit mDisplacementYUnit
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:33
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
void setColor(const QColor &color)
static QString symbolPathToName(QString path)
Get symbols's name from its path.
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
#define DEFAULT_SIMPLEFILL_BORDERSTYLE
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setOffset(QPointF offset)
Sets the offset for the shapeburst fill.
QByteArray mSvgData
SVG data.
Qt::PenStyle borderStyle() const
void setOutlineWidthUnit(QgsSymbolV2::OutputUnit unit)
QgsLineSymbolV2 * mOutline
Custom outline.
#define DEFAULT_SIMPLEFILL_STYLE
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=0)
Create ogr feature style string for pen.
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
void setScaleFactor(double factor)
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:186
QgsSymbolLayerV2 * clone() const
double mOffset
Offset perpendicular to line direction.
static QString encodePoint(QPointF point)
void setReferencePoint1IsCentroid(bool isCentroid)
Sets the starting point of the gradient to be the feature centroid.
QByteArray getImageData(const QString &path) const
Get image data.
QColor dxfColor(const QgsSymbolV2RenderContext &context) const
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
static Qt::PenStyle decodePenStyle(QString str)
QgsVectorColorRampV2 * mTwoColorGradientRamp
void setLineWidthUnit(QgsSymbolV2::OutputUnit unit)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
virtual QgsVectorColorRampV2 * clone() const =0
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
virtual QSet< QString > usedAttributes() const
QPointF offset() const
Returns the offset for the shapeburst fill.
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void applyGradient(const QgsSymbolV2RenderContext &context, QBrush &brush, const QColor &color, const QColor &color2, const GradientColorType &gradientColorType, QgsVectorColorRampV2 *gradientRamp, const GradientType &gradientType, const GradientCoordinateMode &coordinateMode, const GradientSpread &gradientSpread, const QPointF &referencePoint1, const QPointF &referencePoint2, const double angle)
Applies the gradient to a brush.
void dtArrayToQImage(double *array, QImage *im, QgsVectorColorRampV2 *ramp, double layerAlpha=1, bool useWholeShape=true, int maxPixelDistance=0)
static QString symbolNameToPath(QString name)
Get symbol's path from its name.
#define M_PI
QgsSVGFillSymbolLayer(const QString &svgFilePath="", double width=20, double rotation=0.0)
void stopRender(QgsSymbolV2RenderContext &context)
int symbolLayerCount()
Definition: qgssymbolv2.h:84
QgsSymbolV2::OutputUnit mOffsetUnit
void setPainter(QPainter *p)
double rasterScaleFactor() const
ShapeburstColorType colorType() const
Returns the color mode used for the shapeburst fill.
static const bool selectFillStyle
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u)
Returns scale factor painter units -> pixel dimensions.
double mapUnitsPerPixel() const
Return current map units per pixel.
virtual QColor color() const
void applyPattern(QBrush &brush, const QString &svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit, const QColor &svgFillColor, const QColor &svgOutlineColor, double svgOutlineWidth, QgsSymbolV2::OutputUnit svgOutlineWidthUnit, const QgsSymbolV2RenderContext &context)
Applies the svg pattern to the brush.
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setDisplacementXUnit(QgsSymbolV2::OutputUnit unit)
QgsSymbolV2::OutputUnit mOffsetUnit
void renderPoint(const QPointF &point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
virtual void prepareExpressions(const QgsFields *fields, double scale=-1.0)
QgsSymbolLayerV2 * clone() const
void setBorderWidthUnit(QgsSymbolV2::OutputUnit unit)
void startRender(QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit mOffsetUnit
QString mSvgFilePath
Path to the svg file (or empty if constructed directly from data)
QgsSymbolV2::SymbolType type() const
virtual QColor color(double value) const =0
QgsSymbolV2::OutputUnit mOffsetUnit
QgsSymbolV2::OutputUnit mOutlineWidthUnit
double mOutlineWidth
Outline width.
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
double rasterScaleFactor
Definition: qgssvgcache.cpp:80
void setDistanceXUnit(QgsSymbolV2::OutputUnit unit)
double mLineWidth
Line width (in mm or map units)
QgsVectorColorRampV2 * mGradientRamp
QColor svgOutlineColor() const
void startRender(QgsSymbolV2RenderContext &context)
GradientSpread gradientSpread() const
Gradient spread mode.
static QgsSymbolLayerV2 * createLineLayerFromSld(QDomElement &element)
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
QColor color2() const
Returns the color used for the endpoint of the shapeburst fill.
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
void setOffset(QPointF offset)
Offset for gradient fill.
virtual QString layerType() const =0
void stopRender(QgsSymbolV2RenderContext &context)
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
Sets the units used for the offset for the shapeburst fill.
QgsSymbolV2::OutputUnit mDistanceYUnit
virtual QgsSymbolV2 * clone() const
static QgsVectorColorRampV2 * create(const QgsStringMap &properties=QgsStringMap())
void setOffset(QPointF offset)
void applyPattern(const QgsSymbolV2RenderContext &context, QBrush &brush, double distanceX, double distanceY, double displacementX, double displacementY)
A class for svg fill patterns.
void stopRender(QgsSymbolV2RenderContext &context)
bool setSubSymbol(QgsSymbolV2 *symbol)
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, QColor color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=0, const Qt::PenCapStyle *penCapStyle=0, const QVector< qreal > *customDashPattern=0, double dashOffset=0.0)
double widthScaleFactor
Definition: qgssvgcache.cpp:79
static void createGeometryElement(QDomDocument &doc, QDomElement &element, QString geomFunc)
Contains information about the context of a rendering operation.
QColor color2() const
Color for endpoint of gradient, only used if the gradient color type is set to SimpleTwoColor.
QPointF rotateReferencePoint(const QPointF &refPoint, double angle)
rotates a reference point by a specified angle around the point (0.5, 0.5)
QPainter * painter()
void stopRender(QgsRenderContext &context)
void setColor(const QColor &color)
void storeViewBox()
Helper function that gets the view box from the byte array.
static QString encodeBrushStyle(Qt::BrushStyle style)
void startRender(QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit mDistanceXUnit
QgsStringMap properties() const
QgsGradientFillSymbolLayerV2(QColor color=DEFAULT_SIMPLEFILL_COLOR, QColor color2=Qt::white, GradientColorType gradientColorType=SimpleTwoColor, GradientType gradientType=Linear, GradientCoordinateMode coordinateMode=Feature, GradientSpread gradientSpread=Pad)
void setDisplacementYUnit(QgsSymbolV2::OutputUnit unit)
virtual QgsSymbolV2 * clone() const
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:38
void setSvgFilePath(const QString &svgPath)
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:164
QgsSymbolV2::OutputUnit mBorderWidthUnit
Qt::PenJoinStyle penJoinStyle() const
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void applyDataDefinedSettings(const QgsSymbolV2RenderContext &context)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setColorRamp(QgsVectorColorRampV2 *ramp)
Sets the color ramp used to draw the shapeburst fill.
void setMapToPixel(const QgsMapToPixel &mtp)
QgsSymbolV2::OutputUnit outputUnit() const
Definition: qgssymbolv2.cpp:63
static void externalGraphicToSld(QDomDocument &doc, QDomElement &element, QString path, QString mime, QColor color, double size=-1)
QgsSymbolV2::OutputUnit mDistanceUnit
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u)
Returns the line width scale factor depending on the unit and the paint device.
#define INF
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, QString name, QColor color, QColor borderColor=QColor(), double borderWidth=-1, double size=-1)
const QgsMapToPixel & mapToPixel() const
void startRender(QgsSymbolV2RenderContext &context)
void distanceTransform2d(double *im, int width, int height)
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:192
QgsSimpleFillSymbolLayerV2(QColor color=DEFAULT_SIMPLEFILL_COLOR, Qt::BrushStyle style=DEFAULT_SIMPLEFILL_STYLE, QColor borderColor=DEFAULT_SIMPLEFILL_BORDERCOLOR, Qt::PenStyle borderStyle=DEFAULT_SIMPLEFILL_BORDERSTYLE, double borderWidth=DEFAULT_SIMPLEFILL_BORDERWIDTH, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEFILL_JOINSTYLE)
static Qt::PenJoinStyle decodePenJoinStyle(QString str)
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
GradientType gradientType() const
Type of gradient, eg linear or radial.
bool selected() const
Definition: qgssymbolv2.h:176
QgsShapeburstFillSymbolLayerV2(QColor color=DEFAULT_SIMPLEFILL_COLOR, QColor color2=Qt::white, ShapeburstColorType colorType=SimpleTwoColor, int blurRadius=0, bool useWholeShape=true, double maxDistance=5)
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
void stopRender(QgsSymbolV2RenderContext &context)
void setRasterScaleFactor(double factor)
QgsSymbolV2::OutputUnit mDistanceUnit
QgsLineSymbolV2 * mFillLineSymbol
Fill line.
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void applyDataDefinedSettings(const QgsSymbolV2RenderContext &context)
QgsSymbolLayerV2 * symbolLayer(int layer)
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
virtual double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const
void setPatternWidthUnit(QgsSymbolV2::OutputUnit unit)
void stopRender(QgsSymbolV2RenderContext &context)
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=0, Qt::PenCapStyle *penCapStyle=0, QVector< qreal > *customDashPattern=0, double *dashOffset=0)
QgsSymbolV2::OutputUnit mPatternWidthUnit
QgsSymbolLayerV2 * clone() const
void _renderPolygon(QPainter *p, const QPolygonF &points, const QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
Default method to render polygon.
void addStopsToGradient(QGradient *gradient, double alpha=1)
copy color ramp stops to a QGradient
static const bool selectFillBorder
static QPointF pointOnLineWithDistance(const QPointF &startPoint, const QPointF &directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
double size
Definition: qgssvgcache.cpp:77
void setReferencePoint2(QPointF referencePoint)
End point of gradient fill, in the range [0,0] - [1,1].
QgsSymbolLayerV2 * clone() const
#define DEFAULT_SIMPLEFILL_JOINSTYLE
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves data defined properties to string map.
double angle() const
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:123
static void blurImageInPlace(QImage &image, const QRect &rect, int radius, bool alphaOnly)
Blurs an image in place, e.g.
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
static QgsSymbolV2::OutputUnit decodeOutputUnit(QString str)
#define DEFAULT_SIMPLEFILL_BORDERWIDTH
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies data defined properties of this layer to another symbol layer.
static void premultiplyColor(QColor &rgb, int alpha)
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value.
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
Units for gradient fill offset.
Qt::PenStyle dxfPenStyle() const
QRectF mSvgViewBox
SVG view box (to keep the aspect ratio.
void setIgnoreRings(double ignoreRings)
Sets whether the shapeburst fill should ignore polygon rings when calculating the buffered shading...
QImage * mSvgPattern
SVG pattern image.
double maxDistance() const
Returns the maximum distance from the shape's boundary which is shaded.
QColor color() const
double mPatternWidth
Width of the pattern (in output units)
virtual void setDataDefinedProperty(const QString &property, const QString &expressionString)
#define tr(sourceText)
void applyDataDefinedSymbology(QgsSymbolV2RenderContext &context, QBrush &brush, QPen &pen, QPen &selPen)
#define DEFAULT_SIMPLEFILL_COLOR
void startRender(QgsSymbolV2RenderContext &context)
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const