QGIS API Documentation  2.17.0-Master (0497e4a)
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 "qgsgeometry.h"
24 #include "qgsrendercontext.h"
25 #include "qgsproject.h"
26 #include "qgssvgcache.h"
27 #include "qgslogger.h"
28 #include "qgsvectorcolorrampv2.h"
29 #include "qgsunittypes.h"
30 
31 #include <QPainter>
32 #include <QFile>
33 #include <QSvgRenderer>
34 #include <QDomDocument>
35 #include <QDomElement>
36 
37 QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( const QColor& color, Qt::BrushStyle style, const QColor& borderColor, Qt::PenStyle borderStyle, double borderWidth,
38  Qt::PenJoinStyle penJoinStyle )
39  : mBrushStyle( style )
40  , mBorderColor( borderColor )
41  , mBorderStyle( borderStyle )
42  , mBorderWidth( borderWidth )
43  , mBorderWidthUnit( QgsSymbolV2::MM )
44  , mPenJoinStyle( penJoinStyle )
45  , mOffsetUnit( QgsSymbolV2::MM )
46 {
47  mColor = color;
48 }
49 
51 {
52  mBorderWidthUnit = unit;
53  mOffsetUnit = unit;
54 }
55 
57 {
59  if ( mOffsetUnit != unit )
60  {
61  return QgsSymbolV2::Mixed;
62  }
63  return unit;
64 }
65 
67 {
69  mOffsetMapUnitScale = scale;
70 }
71 
73 {
75  {
77  }
78  return QgsMapUnitScale();
79 }
80 
81 void QgsSimpleFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QBrush& brush, QPen& pen, QPen& selPen )
82 {
83  if ( !hasDataDefinedProperties() )
84  return; // shortcut
85 
86  bool ok;
87 
89  {
92  if ( ok )
94  }
96  {
99  if ( ok )
101  }
103  {
106  if ( ok )
108  }
110  {
114  pen.setWidthF( width );
115  selPen.setWidthF( width );
116  }
118  {
121  if ( ok )
122  {
125  }
126  }
128  {
131  if ( ok )
132  {
135  }
136  }
137 }
138 
139 
141 {
143  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
147  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
148  QPointF offset;
149 
150  if ( props.contains( "color" ) )
151  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
152  if ( props.contains( "style" ) )
153  style = QgsSymbolLayerV2Utils::decodeBrushStyle( props["style"] );
154  if ( props.contains( "color_border" ) )
155  {
156  //pre 2.5 projects used "color_border"
157  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
158  }
159  else if ( props.contains( "outline_color" ) )
160  {
161  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
162  }
163  else if ( props.contains( "line_color" ) )
164  {
165  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
166  }
167 
168  if ( props.contains( "style_border" ) )
169  {
170  //pre 2.5 projects used "style_border"
171  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["style_border"] );
172  }
173  else if ( props.contains( "outline_style" ) )
174  {
175  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
176  }
177  else if ( props.contains( "line_style" ) )
178  {
179  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
180  }
181  if ( props.contains( "width_border" ) )
182  {
183  //pre 2.5 projects used "width_border"
184  borderWidth = props["width_border"].toDouble();
185  }
186  else if ( props.contains( "outline_width" ) )
187  {
188  borderWidth = props["outline_width"].toDouble();
189  }
190  else if ( props.contains( "line_width" ) )
191  {
192  borderWidth = props["line_width"].toDouble();
193  }
194  if ( props.contains( "offset" ) )
195  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
196  if ( props.contains( "joinstyle" ) )
197  penJoinStyle = QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] );
198 
199  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, style, borderColor, borderStyle, borderWidth, penJoinStyle );
200  sl->setOffset( offset );
201  if ( props.contains( "border_width_unit" ) )
202  {
203  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["border_width_unit"] ) );
204  }
205  else if ( props.contains( "outline_width_unit" ) )
206  {
207  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
208  }
209  else if ( props.contains( "line_width_unit" ) )
210  {
211  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
212  }
213  if ( props.contains( "offset_unit" ) )
214  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
215 
216  if ( props.contains( "border_width_map_unit_scale" ) )
217  sl->setBorderWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["border_width_map_unit_scale"] ) );
218  if ( props.contains( "offset_map_unit_scale" ) )
219  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
220 
221  sl->restoreDataDefinedProperties( props );
222 
223  return sl;
224 }
225 
226 
228 {
229  return "SimpleFill";
230 }
231 
233 {
235  fillColor.setAlphaF( context.alpha() * mColor.alphaF() );
236  mBrush = QBrush( fillColor, mBrushStyle );
237 
238  // scale brush content for printout
239  double rasterScaleFactor = context.renderContext().rasterScaleFactor();
240  if ( rasterScaleFactor != 1.0 )
241  {
242  mBrush.setMatrix( QMatrix().scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ) );
243  }
244 
245  QColor selColor = context.renderContext().selectionColor();
246  QColor selPenColor = selColor == mColor ? selColor : mBorderColor;
247  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
248  mSelBrush = QBrush( selColor );
249  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
250  // this would mean symbols with "no fill" look the same whether or not they are selected
251  if ( selectFillStyle )
253 
255  borderColor.setAlphaF( context.alpha() * mBorderColor.alphaF() );
256  mPen = QPen( borderColor );
257  mSelPen = QPen( selPenColor );
261  prepareExpressions( context );
262 }
263 
265 {
266  Q_UNUSED( context );
267 }
268 
270 {
271  QPainter* p = context.renderContext().painter();
272  if ( !p )
273  {
274  return;
275  }
276 
277  applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );
278 
279  p->setBrush( context.selected() ? mSelBrush : mBrush );
280  p->setPen( context.selected() ? mSelPen : mPen );
281 
282  QPointF offset;
283  if ( !mOffset.isNull() )
284  {
287  p->translate( offset );
288  }
289 
290  _renderPolygon( p, points, rings, context );
291 
292  if ( !mOffset.isNull() )
293  {
294  p->translate( -offset );
295  }
296 }
297 
299 {
300  QgsStringMap map;
301  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
303  map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
304  map["outline_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mBorderStyle );
305  map["outline_width"] = QString::number( mBorderWidth );
306  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mBorderWidthUnit );
307  map["border_width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mBorderWidthMapUnitScale );
309  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
311  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
313  return map;
314 }
315 
317 {
319  sl->setOffset( mOffset );
320  sl->setOffsetUnit( mOffsetUnit );
325  copyPaintEffect( sl );
326  return sl;
327 }
328 
329 void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
330 {
331  if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
332  return;
333 
334  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
335  if ( !props.value( "uom", "" ).isEmpty() )
336  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
337  element.appendChild( symbolizerElem );
338 
339  // <Geometry>
340  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
341 
342  if ( mBrushStyle != Qt::NoBrush )
343  {
344  // <Fill>
345  QDomElement fillElem = doc.createElement( "se:Fill" );
346  symbolizerElem.appendChild( fillElem );
348  }
349 
350  if ( mBorderStyle != Qt::NoPen )
351  {
352  // <Stroke>
353  QDomElement strokeElem = doc.createElement( "se:Stroke" );
354  symbolizerElem.appendChild( strokeElem );
356  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, mBorderStyle, borderWidth, borderWidth, &mPenJoinStyle );
357  }
358 
359  // <se:Displacement>
361  QgsSymbolLayerV2Utils::createDisplacementElement( doc, symbolizerElem, offset );
362 }
363 
364 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
365 {
366  //brush
367  QString symbolStyle;
369  symbolStyle.append( ';' );
370  //pen
371  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor, mPenJoinStyle ) );
372  return symbolStyle;
373 }
374 
376 {
377  QgsDebugMsg( "Entered." );
378 
380  Qt::BrushStyle fillStyle;
381  Qt::PenStyle borderStyle;
382  double borderWidth;
383 
384  QDomElement fillElem = element.firstChildElement( "Fill" );
385  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
386 
387  QDomElement strokeElem = element.firstChildElement( "Stroke" );
388  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
389 
390  QPointF offset;
392 
393  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
394  sl->setOffset( offset );
395  return sl;
396 }
397 
399 {
400  double penBleed = mBorderStyle == Qt::NoPen ? 0 : ( mBorderWidth / 2.0 );
401  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
402  return penBleed + offsetBleed;
403 }
404 
406 {
407  double width = mBorderWidth;
409  {
412  }
414 }
415 
417 {
419  {
420  bool ok;
423  if ( ok )
424  return QgsSymbolLayerV2Utils::decodeColor( color );
425  }
426  return mBorderColor;
427 }
428 
430 {
431  double angle = mAngle;
433  {
434  context.setOriginalValueVariable( mAngle );
436  }
437  return angle;
438 }
439 
441 {
442  return mBorderStyle;
443 }
444 
446 {
448  {
449  bool ok;
452  if ( ok )
453  return QgsSymbolLayerV2Utils::decodeColor( color );
454  }
455  return mColor;
456 }
457 
459 {
460  return mBrushStyle;
461 }
462 
463 //QgsGradientFillSymbolLayer
464 
466  GradientColorType colorType, GradientType gradientType,
467  GradientCoordinateMode coordinateMode, GradientSpread spread )
468  : mGradientColorType( colorType )
469  , mGradientRamp( nullptr )
470  , mGradientType( gradientType )
471  , mCoordinateMode( coordinateMode )
472  , mGradientSpread( spread )
473  , mReferencePoint1( QPointF( 0.5, 0 ) )
474  , mReferencePoint1IsCentroid( false )
475  , mReferencePoint2( QPointF( 0.5, 1 ) )
476  , mReferencePoint2IsCentroid( false )
477  , mOffsetUnit( QgsSymbolV2::MM )
478 {
479  mColor = color;
480  mColor2 = color2;
481 }
482 
484 {
485  delete mGradientRamp;
486 }
487 
489 {
490  //default to a two-color, linear gradient with feature mode and pad spreading
495  //default to gradient from the default fill color to white
497  QPointF referencePoint1 = QPointF( 0.5, 0 );
498  bool refPoint1IsCentroid = false;
499  QPointF referencePoint2 = QPointF( 0.5, 1 );
500  bool refPoint2IsCentroid = false;
501  double angle = 0;
502  QPointF offset;
503 
504  //update gradient properties from props
505  if ( props.contains( "type" ) )
506  type = static_cast< GradientType >( props["type"].toInt() );
507  if ( props.contains( "coordinate_mode" ) )
508  coordinateMode = static_cast< GradientCoordinateMode >( props["coordinate_mode"].toInt() );
509  if ( props.contains( "spread" ) )
510  gradientSpread = static_cast< GradientSpread >( props["spread"].toInt() );
511  if ( props.contains( "color_type" ) )
512  colorType = static_cast< GradientColorType >( props["color_type"].toInt() );
513  if ( props.contains( "gradient_color" ) )
514  {
515  //pre 2.5 projects used "gradient_color"
516  color = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color"] );
517  }
518  else if ( props.contains( "color" ) )
519  {
520  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
521  }
522  if ( props.contains( "gradient_color2" ) )
523  {
524  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
525  }
526 
527  if ( props.contains( "reference_point1" ) )
528  referencePoint1 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point1"] );
529  if ( props.contains( "reference_point1_iscentroid" ) )
530  refPoint1IsCentroid = props["reference_point1_iscentroid"].toInt();
531  if ( props.contains( "reference_point2" ) )
532  referencePoint2 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point2"] );
533  if ( props.contains( "reference_point2_iscentroid" ) )
534  refPoint2IsCentroid = props["reference_point2_iscentroid"].toInt();
535  if ( props.contains( "angle" ) )
536  angle = props["angle"].toDouble();
537 
538  if ( props.contains( "offset" ) )
539  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
540 
541  //attempt to create color ramp from props
543 
544  //create a new gradient fill layer with desired properties
545  QgsGradientFillSymbolLayerV2* sl = new QgsGradientFillSymbolLayerV2( color, color2, colorType, type, coordinateMode, gradientSpread );
546  sl->setOffset( offset );
547  if ( props.contains( "offset_unit" ) )
548  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
549  if ( props.contains( "offset_map_unit_scale" ) )
550  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
551  sl->setReferencePoint1( referencePoint1 );
552  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
553  sl->setReferencePoint2( referencePoint2 );
554  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
555  sl->setAngle( angle );
556  if ( gradientRamp )
557  sl->setColorRamp( gradientRamp );
558 
559  sl->restoreDataDefinedProperties( props );
560 
561  return sl;
562 }
563 
565 {
566  delete mGradientRamp;
567  mGradientRamp = ramp;
568 }
569 
571 {
572  return "GradientFill";
573 }
574 
575 void QgsGradientFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, const QPolygonF& points )
576 {
578  {
579  //shortcut
582  return;
583  }
584 
585  bool ok;
586 
587  //first gradient color
588  QColor color = mColor;
590  {
593  if ( ok )
594  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
595  }
596 
597  //second gradient color
600  {
603  if ( ok )
604  color2 = QgsSymbolLayerV2Utils::decodeColor( colorString );
605  }
606 
607  //gradient rotation angle
608  double angle = mAngle;
610  {
611  context.setOriginalValueVariable( mAngle );
613  }
614 
615  //gradient type
618  {
620  if ( ok )
621  {
622  if ( currentType == QObject::tr( "linear" ) )
623  {
625  }
626  else if ( currentType == QObject::tr( "radial" ) )
627  {
629  }
630  else if ( currentType == QObject::tr( "conical" ) )
631  {
633  }
634  }
635  }
636 
637  //coordinate mode
640  {
642  if ( ok )
643  {
644  if ( currentCoordMode == QObject::tr( "feature" ) )
645  {
646  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
647  }
648  else if ( currentCoordMode == QObject::tr( "viewport" ) )
649  {
651  }
652  }
653  }
654 
655  //gradient spread
658  {
660  if ( ok )
661  {
662  if ( currentSpread == QObject::tr( "pad" ) )
663  {
665  }
666  else if ( currentSpread == QObject::tr( "repeat" ) )
667  {
669  }
670  else if ( currentSpread == QObject::tr( "reflect" ) )
671  {
673  }
674  }
675  }
676 
677  //reference point 1 x & y
678  double refPoint1X = mReferencePoint1.x();
680  {
681  context.setOriginalValueVariable( refPoint1X );
682  refPoint1X = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_X, context, refPoint1X ).toDouble();
683  }
684  double refPoint1Y = mReferencePoint1.y();
686  {
687  context.setOriginalValueVariable( refPoint1Y );
688  refPoint1Y = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_Y, context, refPoint1Y ).toDouble();
689  }
690  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
692  {
693  context.setOriginalValueVariable( refPoint1IsCentroid );
694  refPoint1IsCentroid = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_ISCENTROID, context, refPoint1IsCentroid ).toBool();
695  }
696 
697  //reference point 2 x & y
698  double refPoint2X = mReferencePoint2.x();
700  {
701  context.setOriginalValueVariable( refPoint2X );
702  refPoint2X = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_X, context, refPoint2X ).toDouble();
703  }
704  double refPoint2Y = mReferencePoint2.y();
706  {
707  context.setOriginalValueVariable( refPoint2Y );
708  refPoint2Y = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_Y, context, refPoint2Y ).toDouble();
709  }
710  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
712  {
713  context.setOriginalValueVariable( refPoint2IsCentroid );
714  refPoint2IsCentroid = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_ISCENTROID, context, refPoint2IsCentroid ).toBool();
715  }
716 
717  if ( refPoint1IsCentroid || refPoint2IsCentroid )
718  {
719  //either the gradient is starting or ending at a centroid, so calculate it
720  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
721  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
722  QRectF bbox = points.boundingRect();
723  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
724  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
725 
726  if ( refPoint1IsCentroid )
727  {
728  refPoint1X = centroidX;
729  refPoint1Y = centroidY;
730  }
731  if ( refPoint2IsCentroid )
732  {
733  refPoint2X = centroidX;
734  refPoint2Y = centroidY;
735  }
736  }
737 
738  //update gradient with data defined values
739  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
740  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
741 }
742 
743 QPointF QgsGradientFillSymbolLayerV2::rotateReferencePoint( QPointF refPoint, double angle )
744 {
745  //rotate a reference point by a specified angle around the point (0.5, 0.5)
746 
747  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
748  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
749  //rotate this line by the current rotation angle
750  refLine.setAngle( refLine.angle() + angle );
751  //get new end point of line
752  QPointF rotatedReferencePoint = refLine.p2();
753  //make sure coords of new end point is within [0, 1]
754  if ( rotatedReferencePoint.x() > 1 )
755  rotatedReferencePoint.setX( 1 );
756  if ( rotatedReferencePoint.x() < 0 )
757  rotatedReferencePoint.setX( 0 );
758  if ( rotatedReferencePoint.y() > 1 )
759  rotatedReferencePoint.setY( 1 );
760  if ( rotatedReferencePoint.y() < 0 )
761  rotatedReferencePoint.setY( 0 );
762 
763  return rotatedReferencePoint;
764 }
765 
766 void QgsGradientFillSymbolLayerV2::applyGradient( const QgsSymbolV2RenderContext &context, QBrush &brush,
771 {
772  //update alpha of gradient colors
774  fillColor.setAlphaF( context.alpha() * fillColor.alphaF() );
775  QColor fillColor2 = color2;
776  fillColor2.setAlphaF( context.alpha() * fillColor2.alphaF() );
777 
778  //rotate reference points
779  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
780  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
781 
782  //create a QGradient with the desired properties
783  QGradient gradient;
784  switch ( gradientType )
785  {
787  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
788  break;
790  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
791  break;
793  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
794  break;
795  }
796  switch ( coordinateMode )
797  {
799  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
800  break;
802  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
803  break;
804  }
805  switch ( gradientSpread )
806  {
808  gradient.setSpread( QGradient::PadSpread );
809  break;
811  gradient.setSpread( QGradient::ReflectSpread );
812  break;
814  gradient.setSpread( QGradient::RepeatSpread );
815  break;
816  }
817 
818  //add stops to gradient
819  if ( gradientColorType == QgsGradientFillSymbolLayerV2::ColorRamp && gradientRamp && gradientRamp->type() == "gradient" )
820  {
821  //color ramp gradient
822  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( gradientRamp );
823  gradRamp->addStopsToGradient( &gradient, context.alpha() );
824  }
825  else
826  {
827  //two color gradient
828  gradient.setColorAt( 0.0, fillColor );
829  gradient.setColorAt( 1.0, fillColor2 );
830  }
831 
832  //update QBrush use gradient
833  brush = QBrush( gradient );
834 }
835 
837 {
838  QColor selColor = context.renderContext().selectionColor();
839  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
840  mSelBrush = QBrush( selColor );
841 
842  //update mBrush to use a gradient fill with specified properties
843  prepareExpressions( context );
844 }
845 
847 {
848  Q_UNUSED( context );
849 }
850 
852 {
853  QPainter* p = context.renderContext().painter();
854  if ( !p )
855  {
856  return;
857  }
858 
859  applyDataDefinedSymbology( context, points );
860 
861  p->setBrush( context.selected() ? mSelBrush : mBrush );
862  p->setPen( Qt::NoPen );
863 
864  QPointF offset;
865  if ( !mOffset.isNull() )
866  {
869  p->translate( offset );
870  }
871 
872  _renderPolygon( p, points, rings, context );
873 
874  if ( !mOffset.isNull() )
875  {
876  p->translate( -offset );
877  }
878 }
879 
881 {
882  QgsStringMap map;
883  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
884  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
885  map["color_type"] = QString::number( mGradientColorType );
886  map["type"] = QString::number( mGradientType );
887  map["coordinate_mode"] = QString::number( mCoordinateMode );
888  map["spread"] = QString::number( mGradientSpread );
889  map["reference_point1"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint1 );
890  map["reference_point1_iscentroid"] = QString::number( mReferencePoint1IsCentroid );
891  map["reference_point2"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint2 );
892  map["reference_point2_iscentroid"] = QString::number( mReferencePoint2IsCentroid );
893  map["angle"] = QString::number( mAngle );
894  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
896  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
898  if ( mGradientRamp )
899  {
900  map.unite( mGradientRamp->properties() );
901  }
902  return map;
903 }
904 
906 {
908  if ( mGradientRamp )
909  sl->setColorRamp( mGradientRamp->clone() );
914  sl->setAngle( mAngle );
915  sl->setOffset( mOffset );
916  sl->setOffsetUnit( mOffsetUnit );
919  copyPaintEffect( sl );
920  return sl;
921 }
922 
924 {
925  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
926  return offsetBleed;
927 }
928 
930 {
931  mOffsetUnit = unit;
932 }
933 
935 {
936  return mOffsetUnit;
937 }
938 
940 {
941  mOffsetMapUnitScale = scale;
942 }
943 
945 {
946  return mOffsetMapUnitScale;
947 }
948 
949 //QgsShapeburstFillSymbolLayer
950 
952  int blurRadius, bool useWholeShape, double maxDistance )
953  : mBlurRadius( blurRadius )
954  , mUseWholeShape( useWholeShape )
955  , mMaxDistance( maxDistance )
956  , mDistanceUnit( QgsSymbolV2::MM )
957  , mColorType( colorType )
958  , mColor2( color2 )
959  , mGradientRamp( nullptr )
960  , mTwoColorGradientRamp( nullptr )
961  , mIgnoreRings( false )
962  , mOffsetUnit( QgsSymbolV2::MM )
963 {
964  mColor = color;
965 }
966 
968 {
969  delete mGradientRamp;
970 }
971 
973 {
974  //default to a two-color gradient
976  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
977  int blurRadius = 0;
978  bool useWholeShape = true;
979  double maxDistance = 5;
980  QPointF offset;
981 
982  //update fill properties from props
983  if ( props.contains( "color_type" ) )
984  {
985  colorType = static_cast< ShapeburstColorType >( props["color_type"].toInt() );
986  }
987  if ( props.contains( "shapeburst_color" ) )
988  {
989  //pre 2.5 projects used "shapeburst_color"
990  color = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color"] );
991  }
992  else if ( props.contains( "color" ) )
993  {
994  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
995  }
996 
997  if ( props.contains( "shapeburst_color2" ) )
998  {
999  //pre 2.5 projects used "shapeburst_color2"
1000  color2 = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color2"] );
1001  }
1002  else if ( props.contains( "gradient_color2" ) )
1003  {
1004  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
1005  }
1006  if ( props.contains( "blur_radius" ) )
1007  {
1008  blurRadius = props["blur_radius"].toInt();
1009  }
1010  if ( props.contains( "use_whole_shape" ) )
1011  {
1012  useWholeShape = props["use_whole_shape"].toInt();
1013  }
1014  if ( props.contains( "max_distance" ) )
1015  {
1016  maxDistance = props["max_distance"].toDouble();
1017  }
1018  if ( props.contains( "offset" ) )
1019  {
1020  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
1021  }
1022 
1023  //attempt to create color ramp from props
1025 
1026  //create a new shapeburst fill layer with desired properties
1027  QgsShapeburstFillSymbolLayerV2* sl = new QgsShapeburstFillSymbolLayerV2( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1028  sl->setOffset( offset );
1029  if ( props.contains( "offset_unit" ) )
1030  {
1031  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
1032  }
1033  if ( props.contains( "distance_unit" ) )
1034  {
1035  sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
1036  }
1037  if ( props.contains( "offset_map_unit_scale" ) )
1038  {
1039  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
1040  }
1041  if ( props.contains( "distance_map_unit_scale" ) )
1042  {
1043  sl->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["distance_map_unit_scale"] ) );
1044  }
1045  if ( props.contains( "ignore_rings" ) )
1046  {
1047  sl->setIgnoreRings( props["ignore_rings"].toInt() );
1048  }
1049  if ( gradientRamp )
1050  {
1051  sl->setColorRamp( gradientRamp );
1052  }
1053 
1054  sl->restoreDataDefinedProperties( props );
1055 
1056  return sl;
1057 }
1058 
1060 {
1061  return "ShapeburstFill";
1062 }
1063 
1065 {
1066  delete mGradientRamp;
1067  mGradientRamp = ramp;
1068 }
1069 
1070 void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
1071  double& maxDistance, bool& ignoreRings )
1072 {
1073  bool ok;
1074 
1075  //first gradient color
1076  color = mColor;
1078  {
1081  if ( ok )
1082  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
1083  }
1084 
1085  //second gradient color
1086  color2 = mColor2;
1088  {
1091  if ( ok )
1092  color2 = QgsSymbolLayerV2Utils::decodeColor( colorString );
1093  }
1094 
1095  //blur radius
1098  {
1101  }
1102 
1103  //use whole shape
1106  {
1109  }
1110 
1111  //max distance
1114  {
1117  }
1118 
1119  //ignore rings
1120  ignoreRings = mIgnoreRings;
1122  {
1125  }
1126 
1127 }
1128 
1130 {
1131  //TODO - check this
1132  QColor selColor = context.renderContext().selectionColor();
1133  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
1134  mSelBrush = QBrush( selColor );
1135 
1136  prepareExpressions( context );
1137 }
1138 
1140 {
1141  Q_UNUSED( context );
1142 }
1143 
1145 {
1146  QPainter* p = context.renderContext().painter();
1147  if ( !p )
1148  {
1149  return;
1150  }
1151 
1152  if ( context.selected() )
1153  {
1154  //feature is selected, draw using selection style
1155  p->setBrush( mSelBrush );
1156  QPointF offset;
1157  if ( !mOffset.isNull() )
1158  {
1161  p->translate( offset );
1162  }
1163  _renderPolygon( p, points, rings, context );
1164  if ( !mOffset.isNull() )
1165  {
1166  p->translate( -offset );
1167  }
1168  return;
1169  }
1170 
1171  QColor color1, color2;
1172  int blurRadius;
1173  bool useWholeShape;
1174  double maxDistance;
1175  bool ignoreRings;
1176  //calculate data defined symbology
1177  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1178 
1179  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1180  int outputPixelMaxDist = 0;
1181  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1182  {
1183  //convert max distance to pixels
1184  const QgsRenderContext& ctx = context.renderContext();
1185  outputPixelMaxDist = maxDistance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit, mDistanceMapUnitScale );
1186  }
1187 
1188  //if we are using the two color mode, create a gradient ramp
1190  {
1191  mTwoColorGradientRamp = new QgsVectorGradientColorRampV2( color1, color2 );
1192  }
1193 
1194  //no border for shapeburst fills
1195  p->setPen( QPen( Qt::NoPen ) );
1196 
1197  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1198  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1199  //create a QImage to draw shapeburst in
1200  double imWidth = points.boundingRect().width() + ( sideBuffer * 2 );
1201  double imHeight = points.boundingRect().height() + ( sideBuffer * 2 );
1202  QImage * fillImage = new QImage( imWidth * context.renderContext().rasterScaleFactor(),
1203  imHeight * context.renderContext().rasterScaleFactor(), QImage::Format_ARGB32_Premultiplied );
1204  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1205  //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
1206  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1207  fillImage->fill( Qt::black );
1208 
1209  //also create an image to store the alpha channel
1210  QImage * alphaImage = new QImage( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1211  //initially fill the alpha channel image with a transparent color
1212  alphaImage->fill( Qt::transparent );
1213 
1214  //now, draw the polygon in the alpha channel image
1215  QPainter imgPainter;
1216  imgPainter.begin( alphaImage );
1217  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1218  imgPainter.setBrush( QBrush( Qt::white ) );
1219  imgPainter.setPen( QPen( Qt::black ) );
1220  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1221  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1222  _renderPolygon( &imgPainter, points, rings, context );
1223  imgPainter.end();
1224 
1225  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1226  //(this avoids calling _renderPolygon twice, since that can be slow)
1227  imgPainter.begin( fillImage );
1228  if ( !ignoreRings )
1229  {
1230  imgPainter.drawImage( 0, 0, *alphaImage );
1231  }
1232  else
1233  {
1234  //using ignore rings mode, so the alpha image can't be used
1235  //directly as the alpha channel contains polygon rings and we need
1236  //to draw now without any rings
1237  imgPainter.setBrush( QBrush( Qt::white ) );
1238  imgPainter.setPen( QPen( Qt::black ) );
1239  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1240  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1241  _renderPolygon( &imgPainter, points, nullptr, context );
1242  }
1243  imgPainter.end();
1244 
1245  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1246  double * dtArray = distanceTransform( fillImage );
1247 
1248  //copy distance transform values back to QImage, shading by appropriate color ramp
1250  context.alpha(), useWholeShape, outputPixelMaxDist );
1251 
1252  //clean up some variables
1253  delete [] dtArray;
1255  {
1256  delete mTwoColorGradientRamp;
1257  }
1258 
1259  //apply blur if desired
1260  if ( blurRadius > 0 )
1261  {
1262  QgsSymbolLayerV2Utils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1263  }
1264 
1265  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1266  imgPainter.begin( fillImage );
1267  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1268  imgPainter.drawImage( 0, 0, *alphaImage );
1269  imgPainter.end();
1270  //we're finished with the alpha channel image now
1271  delete alphaImage;
1272 
1273  //draw shapeburst image in correct place in the destination painter
1274 
1275  p->save();
1276  QPointF offset;
1277  if ( !mOffset.isNull() )
1278  {
1281  p->translate( offset );
1282  }
1283 
1284  p->scale( 1 / context.renderContext().rasterScaleFactor(), 1 / context.renderContext().rasterScaleFactor() );
1285  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1286 
1287  delete fillImage;
1288 
1289  if ( !mOffset.isNull() )
1290  {
1291  p->translate( -offset );
1292  }
1293  p->restore();
1294 
1295 }
1296 
1297 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1298 
1299 /* distance transform of a 1d function using squared distance */
1300 void QgsShapeburstFillSymbolLayerV2::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1301 {
1302  int k = 0;
1303  v[0] = 0;
1304  z[0] = -INF;
1305  z[1] = + INF;
1306  for ( int q = 1; q <= n - 1; q++ )
1307  {
1308  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1309  while ( s <= z[k] )
1310  {
1311  k--;
1312  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1313  }
1314  k++;
1315  v[k] = q;
1316  z[k] = s;
1317  z[k+1] = + INF;
1318  }
1319 
1320  k = 0;
1321  for ( int q = 0; q <= n - 1; q++ )
1322  {
1323  while ( z[k+1] < q )
1324  k++;
1325  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1326  }
1327 }
1328 
1329 /* distance transform of 2d function using squared distance */
1330 void QgsShapeburstFillSymbolLayerV2::distanceTransform2d( double * im, int width, int height )
1331 {
1332  int maxDimension = qMax( width, height );
1333  double *f = new double[ maxDimension ];
1334  int *v = new int[ maxDimension ];
1335  double *z = new double[ maxDimension + 1 ];
1336  double *d = new double[ maxDimension ];
1337 
1338  // transform along columns
1339  for ( int x = 0; x < width; x++ )
1340  {
1341  for ( int y = 0; y < height; y++ )
1342  {
1343  f[y] = im[ x + y * width ];
1344  }
1345  distanceTransform1d( f, height, v, z, d );
1346  for ( int y = 0; y < height; y++ )
1347  {
1348  im[ x + y * width ] = d[y];
1349  }
1350  }
1351 
1352  // transform along rows
1353  for ( int y = 0; y < height; y++ )
1354  {
1355  for ( int x = 0; x < width; x++ )
1356  {
1357  f[x] = im[ x + y*width ];
1358  }
1359  distanceTransform1d( f, width, v, z, d );
1360  for ( int x = 0; x < width; x++ )
1361  {
1362  im[ x + y*width ] = d[x];
1363  }
1364  }
1365 
1366  delete [] d;
1367  delete [] f;
1368  delete [] v;
1369  delete [] z;
1370 }
1371 
1372 /* distance transform of a binary QImage */
1373 double * QgsShapeburstFillSymbolLayerV2::distanceTransform( QImage *im )
1374 {
1375  int width = im->width();
1376  int height = im->height();
1377 
1378  double * dtArray = new double[width * height];
1379 
1380  //load qImage to array
1381  QRgb tmpRgb;
1382  int idx = 0;
1383  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1384  {
1385  const QRgb* scanLine = reinterpret_cast< const QRgb* >( im->constScanLine( heightIndex ) );
1386  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1387  {
1388  tmpRgb = scanLine[widthIndex];
1389  if ( qRed( tmpRgb ) == 0 )
1390  {
1391  //black pixel, so zero distance
1392  dtArray[ idx ] = 0;
1393  }
1394  else
1395  {
1396  //white pixel, so initially set distance as infinite
1397  dtArray[ idx ] = INF;
1398  }
1399  idx++;
1400  }
1401  }
1402 
1403  //calculate squared distance transform
1404  distanceTransform2d( dtArray, width, height );
1405 
1406  return dtArray;
1407 }
1408 
1409 void QgsShapeburstFillSymbolLayerV2::dtArrayToQImage( double * array, QImage *im, QgsVectorColorRampV2* ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1410 {
1411  int width = im->width();
1412  int height = im->height();
1413 
1414  //find maximum distance value
1415  double maxDistanceValue;
1416 
1417  if ( useWholeShape )
1418  {
1419  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1420  double dtMaxValue = array[0];
1421  for ( int i = 1; i < ( width * height ); ++i )
1422  {
1423  if ( array[i] > dtMaxValue )
1424  {
1425  dtMaxValue = array[i];
1426  }
1427  }
1428 
1429  //values in distance transform are squared
1430  maxDistanceValue = sqrt( dtMaxValue );
1431  }
1432  else
1433  {
1434  //use max distance set in symbol properties
1435  maxDistanceValue = maxPixelDistance;
1436  }
1437 
1438  //update the pixels in the provided QImage
1439  int idx = 0;
1440  double squaredVal = 0;
1441  double pixVal = 0;
1442  QColor pixColor;
1443  bool layerHasAlpha = layerAlpha < 1.0;
1444 
1445  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1446  {
1447  QRgb* scanLine = reinterpret_cast< QRgb* >( im->scanLine( heightIndex ) );
1448  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1449  {
1450  //result of distance transform
1451  squaredVal = array[idx];
1452 
1453  //scale result to fit in the range [0, 1]
1454  if ( maxDistanceValue > 0 )
1455  {
1456  pixVal = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1457  }
1458  else
1459  {
1460  pixVal = 1.0;
1461  }
1462 
1463  //convert value to color from ramp
1464  pixColor = ramp->color( pixVal );
1465 
1466  int pixAlpha = pixColor.alpha();
1467  if (( layerHasAlpha ) || ( pixAlpha != 255 ) )
1468  {
1469  //apply layer's transparency to alpha value
1470  double alpha = pixAlpha * layerAlpha;
1471  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1472  QgsSymbolLayerV2Utils::premultiplyColor( pixColor, alpha );
1473  }
1474 
1475  scanLine[widthIndex] = pixColor.rgba();
1476  idx++;
1477  }
1478  }
1479 }
1480 
1482 {
1483  QgsStringMap map;
1484  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1485  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
1486  map["color_type"] = QString::number( mColorType );
1487  map["blur_radius"] = QString::number( mBlurRadius );
1488  map["use_whole_shape"] = QString::number( mUseWholeShape );
1489  map["max_distance"] = QString::number( mMaxDistance );
1490  map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1491  map["distance_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale );
1492  map["ignore_rings"] = QString::number( mIgnoreRings );
1493  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1494  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1495  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1496 
1498 
1499  if ( mGradientRamp )
1500  {
1501  map.unite( mGradientRamp->properties() );
1502  }
1503 
1504  return map;
1505 }
1506 
1508 {
1510  if ( mGradientRamp )
1511  {
1512  sl->setColorRamp( mGradientRamp->clone() );
1513  }
1517  sl->setOffset( mOffset );
1518  sl->setOffsetUnit( mOffsetUnit );
1521  copyPaintEffect( sl );
1522  return sl;
1523 }
1524 
1526 {
1527  double offsetBleed = qMax( mOffset.x(), mOffset.y() );
1528  return offsetBleed;
1529 }
1530 
1532 {
1533  mDistanceUnit = unit;
1534  mOffsetUnit = unit;
1535 }
1536 
1538 {
1539  if ( mDistanceUnit == mOffsetUnit )
1540  {
1541  return mDistanceUnit;
1542  }
1543  return QgsSymbolV2::Mixed;
1544 }
1545 
1547 {
1548  mDistanceMapUnitScale = scale;
1549  mOffsetMapUnitScale = scale;
1550 }
1551 
1553 {
1555  {
1556  return mDistanceMapUnitScale;
1557  }
1558  return QgsMapUnitScale();
1559 }
1560 
1561 
1562 //QgsImageFillSymbolLayer
1563 
1565  : mNextAngle( 0.0 )
1566  , mOutlineWidth( 0.0 )
1567  , mOutlineWidthUnit( QgsSymbolV2::MM )
1568  , mOutline( nullptr )
1569 {
1570  setSubSymbol( new QgsLineSymbolV2() );
1571 }
1572 
1574 {
1575 }
1576 
1578 {
1579  QPainter* p = context.renderContext().painter();
1580  if ( !p )
1581  {
1582  return;
1583  }
1584 
1585  mNextAngle = mAngle;
1586  applyDataDefinedSettings( context );
1587 
1588  p->setPen( QPen( Qt::NoPen ) );
1589 
1590  QTransform bkTransform = mBrush.transform();
1592  {
1593  //transform brush to upper left corner of geometry bbox
1594  QPointF leftCorner = points.boundingRect().topLeft();
1595  QTransform t = mBrush.transform();
1596  t.translate( leftCorner.x(), leftCorner.y() );
1597  mBrush.setTransform( t );
1598  }
1599 
1600  if ( context.selected() )
1601  {
1602  QColor selColor = context.renderContext().selectionColor();
1603  // Alister - this doesn't seem to work here
1604  //if ( ! selectionIsOpaque )
1605  // selColor.setAlphaF( context.alpha() );
1606  p->setBrush( QBrush( selColor ) );
1607  _renderPolygon( p, points, rings, context );
1608  }
1609 
1610  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1611  {
1612  QTransform t = mBrush.transform();
1613  t.rotate( mNextAngle );
1614  mBrush.setTransform( t );
1615  }
1616  p->setBrush( mBrush );
1617  _renderPolygon( p, points, rings, context );
1618  if ( mOutline )
1619  {
1620  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1621  if ( rings )
1622  {
1623  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1624  for ( ; ringIt != rings->constEnd(); ++ringIt )
1625  {
1626  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1627  }
1628  }
1629  }
1630 
1631  mBrush.setTransform( bkTransform );
1632 }
1633 
1635 {
1636  if ( !symbol ) //unset current outline
1637  {
1638  delete mOutline;
1639  mOutline = nullptr;
1640  return true;
1641  }
1642 
1643  if ( symbol->type() != QgsSymbolV2::Line )
1644  {
1645  delete symbol;
1646  return false;
1647  }
1648 
1649  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1650  if ( lineSymbol )
1651  {
1652  delete mOutline;
1653  mOutline = lineSymbol;
1654  return true;
1655  }
1656 
1657  delete symbol;
1658  return false;
1659 }
1660 
1662 {
1663  mOutlineWidthUnit = unit;
1664 }
1665 
1667 {
1668  return mOutlineWidthUnit;
1669 }
1670 
1672 {
1673  mOutlineWidthMapUnitScale = scale;
1674 }
1675 
1677 {
1679 }
1680 
1682 {
1683  if ( mOutline && mOutline->symbolLayer( 0 ) )
1684  {
1685  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
1686  return subLayerBleed;
1687  }
1688  return 0;
1689 }
1690 
1692 {
1693  double width = mOutlineWidth;
1695  {
1698  }
1700 }
1701 
1703 {
1704  Q_UNUSED( context );
1705  if ( !mOutline )
1706  {
1707  return QColor( Qt::black );
1708  }
1709  return mOutline->color();
1710 }
1711 
1713 {
1714  return Qt::SolidLine;
1715 #if 0
1716  if ( !mOutline )
1717  {
1718  return Qt::SolidLine;
1719  }
1720  else
1721  {
1722  return mOutline->dxfPenStyle();
1723  }
1724 #endif //0
1725 }
1726 
1728 {
1730  if ( mOutline )
1731  attr.unite( mOutline->usedAttributes() );
1732  return attr;
1733 }
1734 
1735 
1736 //QgsSVGFillSymbolLayer
1737 
1738 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle )
1740  , mPatternWidth( width )
1741  , mPatternWidthUnit( QgsSymbolV2::MM )
1742  , mSvgOutlineWidthUnit( QgsSymbolV2::MM )
1743 {
1744  setSvgFilePath( svgFilePath );
1745  mOutlineWidth = 0.3;
1746  mAngle = angle;
1747  mColor = QColor( 255, 255, 255 );
1748  mSvgOutlineColor = QColor( 0, 0, 0 );
1749  mSvgOutlineWidth = 0.2;
1750  setDefaultSvgParams();
1751  mSvgPattern = nullptr;
1752 }
1753 
1754 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle )
1756  , mPatternWidth( width )
1758  , mSvgData( svgData )
1760 {
1761  storeViewBox();
1762  mOutlineWidth = 0.3;
1763  mAngle = angle;
1764  mColor = QColor( 255, 255, 255 );
1765  mSvgOutlineColor = QColor( 0, 0, 0 );
1766  mSvgOutlineWidth = 0.2;
1767  setSubSymbol( new QgsLineSymbolV2() );
1768  setDefaultSvgParams();
1769  mSvgPattern = nullptr;
1770 }
1771 
1773 {
1774  delete mSvgPattern;
1775 }
1776 
1778 {
1780  mPatternWidthUnit = unit;
1781  mSvgOutlineWidthUnit = unit;
1782  mOutlineWidthUnit = unit;
1783  mOutline->setOutputUnit( unit );
1784 }
1785 
1787 {
1789  if ( mPatternWidthUnit != unit || mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
1790  {
1791  return QgsSymbolV2::Mixed;
1792  }
1793  return unit;
1794 }
1795 
1797 {
1799  mPatternWidthMapUnitScale = scale;
1801  mOutlineWidthMapUnitScale = scale;
1802 }
1803 
1805 {
1809  {
1811  }
1812  return QgsMapUnitScale();
1813 }
1814 
1816 {
1818  storeViewBox();
1819 
1820  mSvgFilePath = svgPath;
1821  setDefaultSvgParams();
1822 }
1823 
1825 {
1826  QByteArray data;
1827  double width = 20;
1829  double angle = 0.0;
1830 
1831  if ( properties.contains( "width" ) )
1832  {
1833  width = properties["width"].toDouble();
1834  }
1835  if ( properties.contains( "svgFile" ) )
1836  {
1837  QString svgName = properties["svgFile"];
1838  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
1839  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
1840  }
1841  if ( properties.contains( "angle" ) )
1842  {
1843  angle = properties["angle"].toDouble();
1844  }
1845 
1846  QgsSVGFillSymbolLayer* symbolLayer = nullptr;
1847  if ( !svgFilePath.isEmpty() )
1848  {
1849  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1850  }
1851  else
1852  {
1853  if ( properties.contains( "data" ) )
1854  {
1855  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1856  }
1857  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1858  }
1859 
1860  //svg parameters
1861  if ( properties.contains( "svgFillColor" ) )
1862  {
1863  //pre 2.5 projects used "svgFillColor"
1864  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgFillColor"] ) );
1865  }
1866  else if ( properties.contains( "color" ) )
1867  {
1868  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["color"] ) );
1869  }
1870  if ( properties.contains( "svgOutlineColor" ) )
1871  {
1872  //pre 2.5 projects used "svgOutlineColor"
1873  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgOutlineColor"] ) );
1874  }
1875  else if ( properties.contains( "outline_color" ) )
1876  {
1877  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
1878  }
1879  else if ( properties.contains( "line_color" ) )
1880  {
1881  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] ) );
1882  }
1883  if ( properties.contains( "svgOutlineWidth" ) )
1884  {
1885  //pre 2.5 projects used "svgOutlineWidth"
1886  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1887  }
1888  else if ( properties.contains( "outline_width" ) )
1889  {
1890  symbolLayer->setSvgOutlineWidth( properties["outline_width"].toDouble() );
1891  }
1892  else if ( properties.contains( "line_width" ) )
1893  {
1894  symbolLayer->setSvgOutlineWidth( properties["line_width"].toDouble() );
1895  }
1896 
1897  //units
1898  if ( properties.contains( "pattern_width_unit" ) )
1899  {
1900  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1901  }
1902  if ( properties.contains( "pattern_width_map_unit_scale" ) )
1903  {
1904  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["pattern_width_map_unit_scale"] ) );
1905  }
1906  if ( properties.contains( "svg_outline_width_unit" ) )
1907  {
1908  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1909  }
1910  if ( properties.contains( "svg_outline_width_map_unit_scale" ) )
1911  {
1912  symbolLayer->setSvgOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["svg_outline_width_map_unit_scale"] ) );
1913  }
1914  if ( properties.contains( "outline_width_unit" ) )
1915  {
1916  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1917  }
1918  if ( properties.contains( "outline_width_map_unit_scale" ) )
1919  {
1920  symbolLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
1921  }
1922 
1923  symbolLayer->restoreDataDefinedProperties( properties );
1924 
1925  return symbolLayer;
1926 }
1927 
1929 {
1930  return "SVGFill";
1931 }
1932 
1933 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1934  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1937 {
1938  if ( mSvgViewBox.isNull() )
1939  {
1940  return;
1941  }
1942 
1943  delete mSvgPattern;
1944  mSvgPattern = nullptr;
1946 
1947  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1948  {
1949  mSvgPattern = new QImage();
1950  brush.setTextureImage( *mSvgPattern );
1951  }
1952  else
1953  {
1954  bool fitsInCache = true;
1956  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1957  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1958  if ( !fitsInCache )
1959  {
1960  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1961  context.renderContext().scaleFactor(), 1.0 );
1962  double hwRatio = 1.0;
1963  if ( patternPict.width() > 0 )
1964  {
1965  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1966  }
1967  mSvgPattern = new QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1968  mSvgPattern->fill( 0 ); // transparent background
1969 
1970  QPainter p( mSvgPattern );
1971  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1972  }
1973 
1974  QTransform brushTransform;
1975  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1976  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1977  {
1978  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1979  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1980  brush.setTextureImage( transparentImage );
1981  }
1982  else
1983  {
1984  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1985  }
1986  brush.setTransform( brushTransform );
1987  }
1988 }
1989 
1991 {
1992 
1994 
1995  if ( mOutline )
1996  {
1997  mOutline->startRender( context.renderContext(), context.fields() );
1998  }
1999 
2000  prepareExpressions( context );
2001 }
2002 
2004 {
2005  if ( mOutline )
2006  {
2007  mOutline->stopRender( context.renderContext() );
2008  }
2009 }
2010 
2012 {
2013  QgsStringMap map;
2014  if ( !mSvgFilePath.isEmpty() )
2015  {
2017  }
2018  else
2019  {
2020  map.insert( "data", QString( mSvgData.toHex() ) );
2021  }
2022 
2023  map.insert( "width", QString::number( mPatternWidth ) );
2024  map.insert( "angle", QString::number( mAngle ) );
2025 
2026  //svg parameters
2028  map.insert( "outline_color", QgsSymbolLayerV2Utils::encodeColor( mSvgOutlineColor ) );
2029  map.insert( "outline_width", QString::number( mSvgOutlineWidth ) );
2030 
2031  //units
2032  map.insert( "pattern_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit ) );
2033  map.insert( "pattern_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2034  map.insert( "svg_outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit ) );
2035  map.insert( "svg_outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mSvgOutlineWidthMapUnitScale ) );
2036  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2037  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2038 
2040  return map;
2041 }
2042 
2044 {
2045  QgsSVGFillSymbolLayer* clonedLayer = nullptr;
2046  if ( !mSvgFilePath.isEmpty() )
2047  {
2048  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
2049  clonedLayer->setSvgFillColor( mColor );
2050  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
2051  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
2052  }
2053  else
2054  {
2055  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
2056  }
2057 
2058  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2062  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
2064 
2065  if ( mOutline )
2066  {
2067  clonedLayer->setSubSymbol( mOutline->clone() );
2068  }
2069  copyDataDefinedProperties( clonedLayer );
2070  copyPaintEffect( clonedLayer );
2071  return clonedLayer;
2072 }
2073 
2074 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
2075 {
2076  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2077  if ( !props.value( "uom", "" ).isEmpty() )
2078  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2079  element.appendChild( symbolizerElem );
2080 
2081  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2082 
2083  QDomElement fillElem = doc.createElement( "se:Fill" );
2084  symbolizerElem.appendChild( fillElem );
2085 
2086  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2087  fillElem.appendChild( graphicFillElem );
2088 
2089  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2090  graphicFillElem.appendChild( graphicElem );
2091 
2092  if ( !mSvgFilePath.isEmpty() )
2093  {
2094  double partternWidth = QgsSymbolLayerV2Utils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2095  QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, partternWidth );
2096  }
2097  else
2098  {
2099  // TODO: create svg from data
2100  // <se:InlineContent>
2101  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
2102  }
2103 
2104  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
2105  {
2107  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, svgOutlineWidth );
2108  }
2109 
2110  // <Rotation>
2111  QString angleFunc;
2112  bool ok;
2113  double angle = props.value( "angle", "0" ).toDouble( &ok );
2114  if ( !ok )
2115  {
2116  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
2117  }
2118  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2119  {
2120  angleFunc = QString::number( angle + mAngle );
2121  }
2122  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2123 
2124  if ( mOutline )
2125  {
2126  // the outline sub symbol should be stored within the Stroke element,
2127  // but it will be stored in a separated LineSymbolizer because it could
2128  // have more than one layer
2129  mOutline->toSld( doc, element, props );
2130  }
2131 }
2132 
2134 {
2135  QgsDebugMsg( "Entered." );
2136 
2137  QString path, mimeType;
2138  QColor fillColor, borderColor;
2139  Qt::PenStyle penStyle;
2140  double size, borderWidth;
2141 
2142  QDomElement fillElem = element.firstChildElement( "Fill" );
2143  if ( fillElem.isNull() )
2144  return nullptr;
2145 
2146  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2147  if ( graphicFillElem.isNull() )
2148  return nullptr;
2149 
2150  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2151  if ( graphicElem.isNull() )
2152  return nullptr;
2153 
2154  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2155  return nullptr;
2156 
2157  if ( mimeType != "image/svg+xml" )
2158  return nullptr;
2159 
2160  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
2161 
2162  double angle = 0.0;
2163  QString angleFunc;
2164  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2165  {
2166  bool ok;
2167  double d = angleFunc.toDouble( &ok );
2168  if ( ok )
2169  angle = d;
2170  }
2171 
2172  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
2173  sl->setSvgFillColor( fillColor );
2174  sl->setSvgOutlineColor( borderColor );
2175  sl->setSvgOutlineWidth( borderWidth );
2176 
2177  // try to get the outline
2178  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2179  if ( !strokeElem.isNull() )
2180  {
2182  if ( l )
2183  {
2184  QgsSymbolLayerV2List layers;
2185  layers.append( l );
2186  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2187  }
2188  }
2189 
2190  return sl;
2191 }
2192 
2194 {
2198  {
2199  return; //no data defined settings
2200  }
2201 
2202  bool ok;
2203 
2205  {
2206  context.setOriginalValueVariable( mAngle );
2207  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
2208  if ( ok )
2209  mNextAngle = nextAngle;
2210  }
2211 
2212  double width = mPatternWidth;
2214  {
2217  }
2218  QString svgFile = mSvgFilePath;
2220  {
2223  }
2224  QColor svgFillColor = mColor;
2226  {
2229  if ( ok )
2230  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2231  }
2232  QColor svgOutlineColor = mSvgOutlineColor;
2234  {
2237  if ( ok )
2238  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2239  }
2240  double outlineWidth = mSvgOutlineWidth;
2242  {
2245  }
2246  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
2248 
2249 }
2250 
2251 void QgsSVGFillSymbolLayer::storeViewBox()
2252 {
2253  if ( !mSvgData.isEmpty() )
2254  {
2255  QSvgRenderer r( mSvgData );
2256  if ( r.isValid() )
2257  {
2258  mSvgViewBox = r.viewBoxF();
2259  return;
2260  }
2261  }
2262 
2263  mSvgViewBox = QRectF();
2264  return;
2265 }
2266 
2267 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2268 {
2269  if ( mSvgFilePath.isEmpty() )
2270  {
2271  return;
2272  }
2273 
2274  bool hasFillParam, hasFillOpacityParam, hasOutlineParam, hasOutlineWidthParam, hasOutlineOpacityParam;
2275  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultOutlineColor, hasDefaultOutlineWidth, hasDefaultOutlineOpacity;
2276  QColor defaultFillColor, defaultOutlineColor;
2277  double defaultOutlineWidth, defaultFillOpacity, defaultOutlineOpacity;
2278  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2279  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2280  hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
2281  hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
2282  hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
2283 
2284  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2285  double newOutlineOpacity = hasOutlineOpacityParam ? mSvgOutlineColor.alphaF() : 1.0;
2286 
2287  if ( hasDefaultFillColor )
2288  {
2289  mColor = defaultFillColor;
2290  mColor.setAlphaF( newFillOpacity );
2291  }
2292  if ( hasDefaultFillOpacity )
2293  {
2294  mColor.setAlphaF( defaultFillOpacity );
2295  }
2296  if ( hasDefaultOutlineColor )
2297  {
2298  mSvgOutlineColor = defaultOutlineColor;
2299  mSvgOutlineColor.setAlphaF( newOutlineOpacity );
2300  }
2301  if ( hasDefaultOutlineOpacity )
2302  {
2303  mSvgOutlineColor.setAlphaF( defaultOutlineOpacity );
2304  }
2305  if ( hasDefaultOutlineWidth )
2306  {
2307  mSvgOutlineWidth = defaultOutlineWidth;
2308  }
2309 }
2310 
2311 
2314  , mDistance( 5.0 )
2315  , mDistanceUnit( QgsSymbolV2::MM )
2316  , mLineWidth( 0 )
2317  , mLineWidthUnit( QgsSymbolV2::MM )
2318  , mLineAngle( 45.0 )
2319  , mOffset( 0.0 )
2320  , mOffsetUnit( QgsSymbolV2::MM )
2321  , mFillLineSymbol( nullptr )
2322 {
2323  setSubSymbol( new QgsLineSymbolV2() );
2324  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
2325 }
2326 
2328 {
2329  mFillLineSymbol->setWidth( w );
2330  mLineWidth = w;
2331 }
2332 
2334 {
2335  mFillLineSymbol->setColor( c );
2336  mColor = c;
2337 }
2338 
2340 {
2341  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2342 }
2343 
2345 {
2346  delete mFillLineSymbol;
2347 }
2348 
2350 {
2351  if ( !symbol )
2352  {
2353  return false;
2354  }
2355 
2356  if ( symbol->type() == QgsSymbolV2::Line )
2357  {
2358  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
2359  if ( lineSymbol )
2360  {
2361  delete mFillLineSymbol;
2362  mFillLineSymbol = lineSymbol;
2363 
2364  return true;
2365  }
2366  }
2367  delete symbol;
2368  return false;
2369 }
2370 
2372 {
2373  return mFillLineSymbol;
2374 }
2375 
2377 {
2379  if ( mFillLineSymbol )
2380  attr.unite( mFillLineSymbol->usedAttributes() );
2381  return attr;
2382 }
2383 
2385 {
2386  return 0;
2387 }
2388 
2390 {
2392  mDistanceUnit = unit;
2393  mLineWidthUnit = unit;
2394  mOffsetUnit = unit;
2395 }
2396 
2398 {
2400  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2401  {
2402  return QgsSymbolV2::Mixed;
2403  }
2404  return unit;
2405 }
2406 
2408 {
2410  mDistanceMapUnitScale = scale;
2411  mLineWidthMapUnitScale = scale;
2412  mOffsetMapUnitScale = scale;
2413 }
2414 
2416 {
2420  {
2421  return mDistanceMapUnitScale;
2422  }
2423  return QgsMapUnitScale();
2424 }
2425 
2427 {
2429 
2430  //default values
2431  double lineAngle = 45;
2432  double distance = 5;
2433  double lineWidth = 0.5;
2434  QColor color( Qt::black );
2435  double offset = 0.0;
2436 
2437  if ( properties.contains( "lineangle" ) )
2438  {
2439  //pre 2.5 projects used "lineangle"
2440  lineAngle = properties["lineangle"].toDouble();
2441  }
2442  else if ( properties.contains( "angle" ) )
2443  {
2444  lineAngle = properties["angle"].toDouble();
2445  }
2446  patternLayer->setLineAngle( lineAngle );
2447 
2448  if ( properties.contains( "distance" ) )
2449  {
2450  distance = properties["distance"].toDouble();
2451  }
2452  patternLayer->setDistance( distance );
2453 
2454  if ( properties.contains( "linewidth" ) )
2455  {
2456  //pre 2.5 projects used "linewidth"
2457  lineWidth = properties["linewidth"].toDouble();
2458  }
2459  else if ( properties.contains( "outline_width" ) )
2460  {
2461  lineWidth = properties["outline_width"].toDouble();
2462  }
2463  else if ( properties.contains( "line_width" ) )
2464  {
2465  lineWidth = properties["line_width"].toDouble();
2466  }
2467  patternLayer->setLineWidth( lineWidth );
2468 
2469  if ( properties.contains( "color" ) )
2470  {
2471  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
2472  }
2473  else if ( properties.contains( "outline_color" ) )
2474  {
2475  color = QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] );
2476  }
2477  else if ( properties.contains( "line_color" ) )
2478  {
2479  color = QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] );
2480  }
2481  patternLayer->setColor( color );
2482 
2483  if ( properties.contains( "offset" ) )
2484  {
2485  offset = properties["offset"].toDouble();
2486  }
2487  patternLayer->setOffset( offset );
2488 
2489 
2490  if ( properties.contains( "distance_unit" ) )
2491  {
2492  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
2493  }
2494  if ( properties.contains( "distance_map_unit_scale" ) )
2495  {
2496  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_map_unit_scale"] ) );
2497  }
2498  if ( properties.contains( "line_width_unit" ) )
2499  {
2500  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
2501  }
2502  else if ( properties.contains( "outline_width_unit" ) )
2503  {
2504  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2505  }
2506  if ( properties.contains( "line_width_map_unit_scale" ) )
2507  {
2508  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["line_width_map_unit_scale"] ) );
2509  }
2510  if ( properties.contains( "offset_unit" ) )
2511  {
2512  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
2513  }
2514  if ( properties.contains( "offset_map_unit_scale" ) )
2515  {
2516  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
2517  }
2518  if ( properties.contains( "outline_width_unit" ) )
2519  {
2520  patternLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2521  }
2522  if ( properties.contains( "outline_width_map_unit_scale" ) )
2523  {
2524  patternLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
2525  }
2526 
2527  patternLayer->restoreDataDefinedProperties( properties );
2528 
2529  return patternLayer;
2530 }
2531 
2533 {
2534  return "LinePatternFill";
2535 }
2536 
2537 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
2538  double lineWidth, const QColor& color )
2539 {
2540  Q_UNUSED( lineWidth );
2541  Q_UNUSED( color );
2542 
2543  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2544 
2545  if ( !mFillLineSymbol )
2546  {
2547  return;
2548  }
2549  // We have to make a copy because marker intervals will have to be adjusted
2550  QgsLineSymbolV2* fillLineSymbol = mFillLineSymbol->clone();
2551  if ( !fillLineSymbol )
2552  {
2553  return;
2554  }
2555 
2556  const QgsRenderContext& ctx = context.renderContext();
2557  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2560 
2561  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2562  // For marker lines we have to get markers interval.
2563  double outputPixelBleed = 0;
2564  double outputPixelInterval = 0; // maximum interval
2565  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2566  {
2567  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2568  double layerBleed = layer->estimateMaxBleed();
2569  // TODO: to get real bleed we have to scale it using context and units,
2570  // unfortunately estimateMaxBleed() ignore units completely, e.g.
2571  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
2572  // offset regardless units. This has to be fixed especially
2573  // in estimateMaxBleed(), context probably has to be used.
2574  // For now, we only support millimeters
2575  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
2576  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
2577 
2578  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2579  if ( markerLineLayer )
2580  {
2581  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2582 
2583  // There may be multiple marker lines with different intervals.
2584  // In theory we should find the least common multiple, but that could be too
2585  // big (multiplication of intervals in the worst case).
2586  // Because patterns without small common interval would look strange, we
2587  // believe that the longest interval should usually be sufficient.
2588  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
2589  }
2590  }
2591 
2592  if ( outputPixelInterval > 0 )
2593  {
2594  // We have to adjust marker intervals to integer pixel size to get
2595  // repeatable pattern.
2596  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
2597  outputPixelInterval = qRound( outputPixelInterval );
2598 
2599  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2600  {
2601  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2602 
2603  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2604  if ( markerLineLayer )
2605  {
2606  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2607  }
2608  }
2609  }
2610 
2611  //create image
2612  int height, width;
2613  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2614  {
2615  height = outputPixelDist;
2616  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2617  }
2618  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2619  {
2620  width = outputPixelDist;
2621  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2622  }
2623  else
2624  {
2625  height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2626  width = outputPixelDist / sin( lineAngle * M_PI / 180 );
2627 
2628  // recalculate real angle and distance after rounding to pixels
2629  lineAngle = 180 * atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2630  if ( lineAngle < 0 )
2631  {
2632  lineAngle += 360.;
2633  }
2634 
2635  height = qAbs( height );
2636  width = qAbs( width );
2637 
2638  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
2639 
2640  // Round offset to correspond to one pixel height, otherwise lines may
2641  // be shifted on tile border if offset falls close to pixel center
2642  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
2643  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
2644  }
2645 
2646  //depending on the angle, we might need to render into a larger image and use a subset of it
2647  double dx = 0;
2648  double dy = 0;
2649 
2650  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2651  // thus we add integer multiplications of width and height covering the bleed
2652  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
2653 
2654  // Always buffer at least once so that center of line marker in upper right corner
2655  // does not fall outside due to representation error
2656  bufferMulti = qMax( bufferMulti, 1 );
2657 
2658  int xBuffer = width * bufferMulti;
2659  int yBuffer = height * bufferMulti;
2660  int innerWidth = width;
2661  int innerHeight = height;
2662  width += 2 * xBuffer;
2663  height += 2 * yBuffer;
2664 
2665  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2666  {
2667  return;
2668  }
2669 
2670  QImage patternImage( width, height, QImage::Format_ARGB32 );
2671  patternImage.fill( 0 );
2672 
2673  QPointF p1, p2, p3, p4, p5, p6;
2674  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2675  {
2676  p1 = QPointF( 0, yBuffer );
2677  p2 = QPointF( width, yBuffer );
2678  p3 = QPointF( 0, yBuffer + innerHeight );
2679  p4 = QPointF( width, yBuffer + innerHeight );
2680  }
2681  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2682  {
2683  p1 = QPointF( xBuffer, height );
2684  p2 = QPointF( xBuffer, 0 );
2685  p3 = QPointF( xBuffer + innerWidth, height );
2686  p4 = QPointF( xBuffer + innerWidth, 0 );
2687  }
2688  else if ( lineAngle > 0 && lineAngle < 90 )
2689  {
2690  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2691  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2692  p1 = QPointF( 0, height );
2693  p2 = QPointF( width, 0 );
2694  p3 = QPointF( -dx, height - dy );
2695  p4 = QPointF( width - dx, -dy );
2696  p5 = QPointF( dx, height + dy );
2697  p6 = QPointF( width + dx, dy );
2698  }
2699  else if ( lineAngle > 180 && lineAngle < 270 )
2700  {
2701  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2702  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2703  p1 = QPointF( width, 0 );
2704  p2 = QPointF( 0, height );
2705  p3 = QPointF( width - dx, -dy );
2706  p4 = QPointF( -dx, height - dy );
2707  p5 = QPointF( width + dx, dy );
2708  p6 = QPointF( dx, height + dy );
2709  }
2710  else if ( lineAngle > 90 && lineAngle < 180 )
2711  {
2712  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2713  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2714  p1 = QPointF( 0, 0 );
2715  p2 = QPointF( width, height );
2716  p5 = QPointF( dx, -dy );
2717  p6 = QPointF( width + dx, height - dy );
2718  p3 = QPointF( -dx, dy );
2719  p4 = QPointF( width - dx, height + dy );
2720  }
2721  else if ( lineAngle > 270 && lineAngle < 360 )
2722  {
2723  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2724  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2725  p1 = QPointF( width, height );
2726  p2 = QPointF( 0, 0 );
2727  p5 = QPointF( width + dx, height - dy );
2728  p6 = QPointF( dx, -dy );
2729  p3 = QPointF( width - dx, height + dy );
2730  p4 = QPointF( -dx, dy );
2731  }
2732 
2733  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2734  {
2735  QPointF tempPt;
2736  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2737  p3 = QPointF( tempPt.x(), tempPt.y() );
2738  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2739  p4 = QPointF( tempPt.x(), tempPt.y() );
2740  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2741  p5 = QPointF( tempPt.x(), tempPt.y() );
2742  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2743  p6 = QPointF( tempPt.x(), tempPt.y() );
2744 
2745  //update p1, p2 last
2746  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2747  p1 = QPointF( tempPt.x(), tempPt.y() );
2748  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2749  p2 = QPointF( tempPt.x(), tempPt.y() );
2750  }
2751 
2752  QPainter p( &patternImage );
2753 
2754 #if 0
2755  // DEBUG: Draw rectangle
2756  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2757  QPen pen( QColor( Qt::black ) );
2758  pen.setWidthF( 0.1 );
2759  pen.setCapStyle( Qt::FlatCap );
2760  p.setPen( pen );
2761 
2762  // To see this rectangle, comment buffer cut below.
2763  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2764  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2765  p.drawPolygon( polygon );
2766 
2767  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 );
2768  p.drawPolygon( polygon );
2769 #endif
2770 
2771  // Use antialiasing because without antialiasing lines are rendered to the
2772  // right and below the mathematically defined points (not symmetrical)
2773  // and such tiles become useless for are filling
2774  p.setRenderHint( QPainter::Antialiasing, true );
2775 
2776  // line rendering needs context for drawing on patternImage
2777  QgsRenderContext lineRenderContext;
2778  lineRenderContext.setPainter( &p );
2779  lineRenderContext.setRasterScaleFactor( 1.0 );
2780  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2782  lineRenderContext.setMapToPixel( mtp );
2783  lineRenderContext.setForceVectorOutput( false );
2784  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2785 
2786  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2787 
2788  QVector<QPolygonF> polygons;
2789  polygons.append( QPolygonF() << p1 << p2 );
2790  polygons.append( QPolygonF() << p3 << p4 );
2791  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2792  {
2793  polygons.append( QPolygonF() << p5 << p6 );
2794  }
2795 
2796  Q_FOREACH ( const QPolygonF& polygon, polygons )
2797  {
2798  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2799  }
2800 
2801  fillLineSymbol->stopRender( lineRenderContext );
2802  p.end();
2803 
2804  // Cut off the buffer
2805  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2806 
2807  //set image to mBrush
2808  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2809  {
2810  QImage transparentImage = patternImage.copy();
2811  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2812  brush.setTextureImage( transparentImage );
2813  }
2814  else
2815  {
2816  brush.setTextureImage( patternImage );
2817  }
2818 
2819  QTransform brushTransform;
2820  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2821  brush.setTransform( brushTransform );
2822 
2823  delete fillLineSymbol;
2824 }
2825 
2827 {
2828  applyPattern( context, mBrush, mLineAngle, mDistance, mLineWidth, mColor );
2829 
2830  if ( mFillLineSymbol )
2831  {
2832  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2833  }
2834 
2835  prepareExpressions( context );
2836 }
2837 
2839 {
2840 }
2841 
2843 {
2844  QgsStringMap map;
2845  map.insert( "angle", QString::number( mLineAngle ) );
2846  map.insert( "distance", QString::number( mDistance ) );
2847  map.insert( "line_width", QString::number( mLineWidth ) );
2849  map.insert( "offset", QString::number( mOffset ) );
2851  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
2853  map.insert( "distance_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2854  map.insert( "line_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2855  map.insert( "offset_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2856  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2857  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2859  return map;
2860 }
2861 
2863 {
2865  if ( mFillLineSymbol )
2866  {
2867  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2868  }
2869  copyPaintEffect( clonedLayer );
2870  return clonedLayer;
2871 }
2872 
2874 {
2875  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2876  if ( !props.value( "uom", "" ).isEmpty() )
2877  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2878  element.appendChild( symbolizerElem );
2879 
2880  // <Geometry>
2881  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2882 
2883  QDomElement fillElem = doc.createElement( "se:Fill" );
2884  symbolizerElem.appendChild( fillElem );
2885 
2886  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2887  fillElem.appendChild( graphicFillElem );
2888 
2889  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2890  graphicFillElem.appendChild( graphicElem );
2891 
2892  //line properties must be inside the graphic definition
2893  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2894  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2895  lineWidth = QgsSymbolLayerV2Utils::rescaleUom( lineWidth, mLineWidthUnit, props );
2897  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2898 
2899  // <Rotation>
2900  QString angleFunc;
2901  bool ok;
2902  double angle = props.value( "angle", "0" ).toDouble( &ok );
2903  if ( !ok )
2904  {
2905  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
2906  }
2907  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2908  {
2909  angleFunc = QString::number( angle + mLineAngle );
2910  }
2911  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2912 
2913  // <se:Displacement>
2914  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2915  lineOffset = QgsSymbolLayerV2Utils::rescaleUom( lineOffset, mOffsetUnit, props );
2916  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
2917 }
2918 
2920 {
2921  QString featureStyle;
2922  featureStyle.append( "Brush(" );
2923  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
2924  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
2925  featureStyle.append( ",id:\"ogr-brush-2\"" );
2926  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
2927  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2928  featureStyle.append( ",dx:0mm" );
2929  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2930  featureStyle.append( ')' );
2931  return featureStyle;
2932 }
2933 
2935 {
2938  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2939  {
2940  return; //no data defined settings
2941  }
2942 
2943  bool ok;
2944  double lineAngle = mLineAngle;
2946  {
2949  }
2950  double distance = mDistance;
2952  {
2955  }
2956  double lineWidth = mLineWidth;
2958  {
2961  }
2962  QColor color = mColor;
2964  {
2967  if ( ok )
2968  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
2969  }
2970  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
2971 }
2972 
2974 {
2975  QgsDebugMsg( "Entered." );
2976 
2977  QString name;
2978  QColor fillColor, lineColor;
2979  double size, lineWidth;
2980  Qt::PenStyle lineStyle;
2981 
2982  QDomElement fillElem = element.firstChildElement( "Fill" );
2983  if ( fillElem.isNull() )
2984  return nullptr;
2985 
2986  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2987  if ( graphicFillElem.isNull() )
2988  return nullptr;
2989 
2990  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2991  if ( graphicElem.isNull() )
2992  return nullptr;
2993 
2994  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2995  return nullptr;
2996 
2997  if ( name != "horline" )
2998  return nullptr;
2999 
3000  double angle = 0.0;
3001  QString angleFunc;
3002  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
3003  {
3004  bool ok;
3005  double d = angleFunc.toDouble( &ok );
3006  if ( ok )
3007  angle = d;
3008  }
3009 
3010  double offset = 0.0;
3011  QPointF vectOffset;
3012  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
3013  {
3014  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
3015  }
3016 
3018  sl->setColor( lineColor );
3019  sl->setLineWidth( lineWidth );
3020  sl->setLineAngle( angle );
3021  sl->setOffset( offset );
3022  sl->setDistance( size );
3023 
3024  // try to get the outline
3025  QDomElement strokeElem = element.firstChildElement( "Stroke" );
3026  if ( !strokeElem.isNull() )
3027  {
3029  if ( l )
3030  {
3031  QgsSymbolLayerV2List layers;
3032  layers.append( l );
3033  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
3034  }
3035  }
3036 
3037  return sl;
3038 }
3039 
3040 
3042 
3045  , mMarkerSymbol( nullptr )
3046  , mDistanceX( 15 )
3047  , mDistanceXUnit( QgsSymbolV2::MM )
3048  , mDistanceY( 15 )
3049  , mDistanceYUnit( QgsSymbolV2::MM )
3050  , mDisplacementX( 0 )
3051  , mDisplacementXUnit( QgsSymbolV2::MM )
3052  , mDisplacementY( 0 )
3053  , mDisplacementYUnit( QgsSymbolV2::MM )
3054 {
3055  mDistanceX = 15;
3056  mDistanceY = 15;
3057  mDisplacementX = 0;
3058  mDisplacementY = 0;
3060  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
3061 }
3062 
3064 {
3065  delete mMarkerSymbol;
3066 }
3067 
3069 {
3071  mDistanceXUnit = unit;
3072  mDistanceYUnit = unit;
3073  mDisplacementXUnit = unit;
3074  mDisplacementYUnit = unit;
3075  if ( mMarkerSymbol )
3076  {
3077  mMarkerSymbol->setOutputUnit( unit );
3078  }
3079 
3080 }
3081 
3083 {
3085  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3086  {
3087  return QgsSymbolV2::Mixed;
3088  }
3089  return unit;
3090 }
3091 
3093 {
3095  mDistanceXMapUnitScale = scale;
3096  mDistanceYMapUnitScale = scale;
3099 }
3100 
3102 {
3107  {
3108  return mDistanceXMapUnitScale;
3109  }
3110  return QgsMapUnitScale();
3111 }
3112 
3114 {
3116  if ( properties.contains( "distance_x" ) )
3117  {
3118  layer->setDistanceX( properties["distance_x"].toDouble() );
3119  }
3120  if ( properties.contains( "distance_y" ) )
3121  {
3122  layer->setDistanceY( properties["distance_y"].toDouble() );
3123  }
3124  if ( properties.contains( "displacement_x" ) )
3125  {
3126  layer->setDisplacementX( properties["displacement_x"].toDouble() );
3127  }
3128  if ( properties.contains( "displacement_y" ) )
3129  {
3130  layer->setDisplacementY( properties["displacement_y"].toDouble() );
3131  }
3132 
3133  if ( properties.contains( "distance_x_unit" ) )
3134  {
3135  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
3136  }
3137  if ( properties.contains( "distance_x_map_unit_scale" ) )
3138  {
3139  layer->setDistanceXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_x_map_unit_scale"] ) );
3140  }
3141  if ( properties.contains( "distance_y_unit" ) )
3142  {
3143  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
3144  }
3145  if ( properties.contains( "distance_y_map_unit_scale" ) )
3146  {
3147  layer->setDistanceYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_y_map_unit_scale"] ) );
3148  }
3149  if ( properties.contains( "displacement_x_unit" ) )
3150  {
3151  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
3152  }
3153  if ( properties.contains( "displacement_x_map_unit_scale" ) )
3154  {
3155  layer->setDisplacementXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_x_map_unit_scale"] ) );
3156  }
3157  if ( properties.contains( "displacement_y_unit" ) )
3158  {
3159  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
3160  }
3161  if ( properties.contains( "displacement_y_map_unit_scale" ) )
3162  {
3163  layer->setDisplacementYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_y_map_unit_scale"] ) );
3164  }
3165  if ( properties.contains( "outline_width_unit" ) )
3166  {
3167  layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
3168  }
3169  if ( properties.contains( "outline_width_map_unit_scale" ) )
3170  {
3171  layer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
3172  }
3173 
3174  layer->restoreDataDefinedProperties( properties );
3175 
3176  return layer;
3177 }
3178 
3180 {
3181  return "PointPatternFill";
3182 }
3183 
3184 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
3185  double displacementX, double displacementY )
3186 {
3187  //render 3 rows and columns in one go to easily incorporate displacement
3188  const QgsRenderContext& ctx = context.renderContext();
3191 
3192  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3193  {
3194  QImage img;
3195  brush.setTextureImage( img );
3196  return;
3197  }
3198 
3199  QImage patternImage( width, height, QImage::Format_ARGB32 );
3200  patternImage.fill( 0 );
3201 
3202  if ( mMarkerSymbol )
3203  {
3204  QPainter p( &patternImage );
3205 
3206  //marker rendering needs context for drawing on patternImage
3207  QgsRenderContext pointRenderContext;
3208  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3209  pointRenderContext.setPainter( &p );
3210  pointRenderContext.setRasterScaleFactor( 1.0 );
3211  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
3213  pointRenderContext.setMapToPixel( mtp );
3214  pointRenderContext.setForceVectorOutput( false );
3215  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3216 
3217  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3218 
3219  //render corner points
3220  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3221  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3222  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3223  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3224 
3225  //render displaced points
3227  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit, mDisplacementYMapUnitScale );
3228  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3229  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3230  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3231  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3232  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3233 
3234  mMarkerSymbol->stopRender( pointRenderContext );
3235  }
3236 
3237  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
3238  {
3239  QImage transparentImage = patternImage.copy();
3240  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
3241  brush.setTextureImage( transparentImage );
3242  }
3243  else
3244  {
3245  brush.setTextureImage( patternImage );
3246  }
3247  QTransform brushTransform;
3248  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
3249  brush.setTransform( brushTransform );
3250 }
3251 
3253 {
3254  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3255 
3256  if ( mOutline )
3257  {
3258  mOutline->startRender( context.renderContext(), context.fields() );
3259  }
3260  prepareExpressions( context );
3261 }
3262 
3264 {
3265  if ( mOutline )
3266  {
3267  mOutline->stopRender( context.renderContext() );
3268  }
3269 }
3270 
3272 {
3273  QgsStringMap map;
3274  map.insert( "distance_x", QString::number( mDistanceX ) );
3275  map.insert( "distance_y", QString::number( mDistanceY ) );
3276  map.insert( "displacement_x", QString::number( mDisplacementX ) );
3277  map.insert( "displacement_y", QString::number( mDisplacementY ) );
3278  map.insert( "distance_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit ) );
3279  map.insert( "distance_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit ) );
3280  map.insert( "displacement_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit ) );
3281  map.insert( "displacement_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit ) );
3282  map.insert( "distance_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3283  map.insert( "distance_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3284  map.insert( "displacement_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3285  map.insert( "displacement_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3286  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
3287  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
3289  return map;
3290 }
3291 
3293 {
3295  if ( mMarkerSymbol )
3296  {
3297  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3298  }
3299  copyPaintEffect( clonedLayer );
3300  return clonedLayer;
3301 }
3302 
3304 {
3305  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3306  {
3307  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
3308  if ( !props.value( "uom", "" ).isEmpty() )
3309  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
3310  element.appendChild( symbolizerElem );
3311 
3312  // <Geometry>
3313  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
3314 
3315  QDomElement fillElem = doc.createElement( "se:Fill" );
3316  symbolizerElem.appendChild( fillElem );
3317 
3318  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
3319  fillElem.appendChild( graphicFillElem );
3320 
3321  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3325  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
3326  symbolizerElem.appendChild( distanceElem );
3327 
3329  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
3330  if ( !markerLayer )
3331  {
3332  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3333  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3334  }
3335  else
3336  {
3337  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3338  }
3339  }
3340 }
3341 
3343 {
3344  Q_UNUSED( element );
3345  return nullptr;
3346 }
3347 
3349 {
3350  if ( !symbol )
3351  {
3352  return false;
3353  }
3354 
3355  if ( symbol->type() == QgsSymbolV2::Marker )
3356  {
3357  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
3358  delete mMarkerSymbol;
3359  mMarkerSymbol = markerSymbol;
3360  }
3361  return true;
3362 }
3363 
3365 {
3369  {
3370  return;
3371  }
3372 
3373  double distanceX = mDistanceX;
3375  {
3378  }
3379  double distanceY = mDistanceY;
3381  {
3384  }
3385  double displacementX = mDisplacementX;
3387  {
3390  }
3391  double displacementY = mDisplacementY;
3393  {
3396  }
3397  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3398 }
3399 
3401 {
3402  return 0;
3403 }
3404 
3406 {
3408 
3409  if ( mMarkerSymbol )
3410  attributes.unite( mMarkerSymbol->usedAttributes() );
3411 
3412  return attributes;
3413 }
3414 
3416 {
3417  mColor = c;
3418  if ( mMarkerSymbol )
3419  mMarkerSymbol->setColor( c );
3420 }
3421 
3423 {
3424  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3425 }
3426 
3428 
3429 
3431  : mMarker( nullptr )
3432  , mPointOnSurface( false )
3433  , mPointOnAllParts( true )
3434  , mCurrentFeatureId( -1 )
3435  , mBiggestPartIndex( -1 )
3436 {
3438 }
3439 
3441 {
3442  delete mMarker;
3443 }
3444 
3446 {
3448 
3449  if ( properties.contains( "point_on_surface" ) )
3450  sl->setPointOnSurface( properties["point_on_surface"].toInt() != 0 );
3451  if ( properties.contains( "point_on_all_parts" ) )
3452  sl->setPointOnAllParts( properties["point_on_all_parts"].toInt() != 0 );
3453 
3454  return sl;
3455 }
3456 
3458 {
3459  return "CentroidFill";
3460 }
3461 
3463 {
3464  mMarker->setColor( color );
3465  mColor = color;
3466 }
3467 
3469 {
3470  return mMarker ? mMarker->color() : mColor;
3471 }
3472 
3474 {
3475  mMarker->setAlpha( context.alpha() );
3476  mMarker->startRender( context.renderContext(), context.fields() );
3477 
3478  mCurrentFeatureId = -1;
3479  mBiggestPartIndex = 0;
3480 }
3481 
3483 {
3484  mMarker->stopRender( context.renderContext() );
3485 }
3486 
3488 {
3489  Q_UNUSED( rings );
3490 
3491  if ( !mPointOnAllParts )
3492  {
3493  const QgsFeature* feature = context.feature();
3494  if ( feature )
3495  {
3496  if ( feature->id() != mCurrentFeatureId )
3497  {
3498  mCurrentFeatureId = feature->id();
3499  mBiggestPartIndex = 1;
3500 
3501  if ( context.geometryPartCount() > 1 )
3502  {
3503  const QgsGeometry *geom = feature->constGeometry();
3504  const QgsGeometryCollectionV2* geomCollection = static_cast<const QgsGeometryCollectionV2*>( geom->geometry() );
3505 
3506  double area = 0;
3507  double areaBiggest = 0;
3508  for ( int i = 0; i < context.geometryPartCount(); ++i )
3509  {
3510  area = geomCollection->geometryN( i )->area();
3511  if ( area > areaBiggest )
3512  {
3513  areaBiggest = area;
3514  mBiggestPartIndex = i + 1;
3515  }
3516  }
3517  }
3518  }
3519  }
3520  }
3521 
3522  QgsDebugMsg( QString( "num: %1, count: %2" ).arg( context.geometryPartNum() ).arg( context.geometryPartCount() ) );
3523 
3524  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3525  {
3527  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3528  }
3529 }
3530 
3532 {
3533  QgsStringMap map;
3534  map["point_on_surface"] = QString::number( mPointOnSurface );
3535  map["point_on_all_parts"] = QString::number( mPointOnAllParts );
3536  return map;
3537 }
3538 
3540 {
3542  x->mAngle = mAngle;
3543  x->mColor = mColor;
3544  x->setSubSymbol( mMarker->clone() );
3547  copyPaintEffect( x );
3548  return x;
3549 }
3550 
3552 {
3553  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3554  // used with PointSymbolizer, then the semantic is to use the centroid
3555  // of the geometry, or any similar representative point.
3556  mMarker->toSld( doc, element, props );
3557 }
3558 
3560 {
3561  QgsDebugMsg( "Entered." );
3562 
3564  if ( !l )
3565  return nullptr;
3566 
3567  QgsSymbolLayerV2List layers;
3568  layers.append( l );
3569  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
3570 
3572  sl->setSubSymbol( marker );
3573  return sl;
3574 }
3575 
3576 
3578 {
3579  return mMarker;
3580 }
3581 
3583 {
3584  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
3585  {
3586  delete symbol;
3587  return false;
3588  }
3589 
3590  delete mMarker;
3591  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
3592  mColor = mMarker->color();
3593  return true;
3594 }
3595 
3597 {
3599 
3600  if ( mMarker )
3601  attributes.unite( mMarker->usedAttributes() );
3602 
3603  return attributes;
3604 }
3605 
3607 {
3608  if ( mMarker )
3609  {
3610  mMarker->setOutputUnit( unit );
3611  }
3612 }
3613 
3615 {
3616  if ( mMarker )
3617  {
3618  return mMarker->outputUnit();
3619  }
3620  return QgsSymbolV2::Mixed; //mOutputUnit;
3621 }
3622 
3624 {
3625  if ( mMarker )
3626  {
3627  mMarker->setMapUnitScale( scale );
3628  }
3629 }
3630 
3632 {
3633  if ( mMarker )
3634  {
3635  return mMarker->mapUnitScale();
3636  }
3637  return QgsMapUnitScale();
3638 }
3639 
3640 
3641 
3642 
3645  , mImageFilePath( imageFilePath )
3646  , mCoordinateMode( QgsRasterFillSymbolLayer::Feature )
3647  , mAlpha( 1.0 )
3648  , mOffsetUnit( QgsSymbolV2::MM )
3649  , mWidth( 0.0 )
3650  , mWidthUnit( QgsSymbolV2::Pixel )
3651 {
3652  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3653 }
3654 
3656 {
3657 
3658 }
3659 
3661 {
3663  double alpha = 1.0;
3664  QPointF offset;
3665  double angle = 0.0;
3666  double width = 0.0;
3667 
3668  QString imagePath;
3669  if ( properties.contains( "imageFile" ) )
3670  {
3671  imagePath = properties["imageFile"];
3672  }
3673  if ( properties.contains( "coordinate_mode" ) )
3674  {
3675  mode = static_cast< FillCoordinateMode >( properties["coordinate_mode"].toInt() );
3676  }
3677  if ( properties.contains( "alpha" ) )
3678  {
3679  alpha = properties["alpha"].toDouble();
3680  }
3681  if ( properties.contains( "offset" ) )
3682  {
3683  offset = QgsSymbolLayerV2Utils::decodePoint( properties["offset"] );
3684  }
3685  if ( properties.contains( "angle" ) )
3686  {
3687  angle = properties["angle"].toDouble();
3688  }
3689  if ( properties.contains( "width" ) )
3690  {
3691  width = properties["width"].toDouble();
3692  }
3693  QgsRasterFillSymbolLayer* symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
3694  symbolLayer->setCoordinateMode( mode );
3695  symbolLayer->setAlpha( alpha );
3696  symbolLayer->setOffset( offset );
3697  symbolLayer->setAngle( angle );
3698  symbolLayer->setWidth( width );
3699  if ( properties.contains( "offset_unit" ) )
3700  {
3701  symbolLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
3702  }
3703  if ( properties.contains( "offset_map_unit_scale" ) )
3704  {
3705  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
3706  }
3707  if ( properties.contains( "width_unit" ) )
3708  {
3709  symbolLayer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["width_unit"] ) );
3710  }
3711  if ( properties.contains( "width_map_unit_scale" ) )
3712  {
3713  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["width_map_unit_scale"] ) );
3714  }
3715 
3716  symbolLayer->restoreDataDefinedProperties( properties );
3717 
3718  return symbolLayer;
3719 }
3720 
3722 {
3723  Q_UNUSED( symbol );
3724  return true;
3725 }
3726 
3728 {
3729  return "RasterFill";
3730 }
3731 
3733 {
3734  QPainter* p = context.renderContext().painter();
3735  if ( !p )
3736  {
3737  return;
3738  }
3739 
3740  QPointF offset;
3741  if ( !mOffset.isNull() )
3742  {
3745  p->translate( offset );
3746  }
3747  if ( mCoordinateMode == Feature )
3748  {
3749  QRectF boundingRect = points.boundingRect();
3750  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3751  boundingRect.top() - mBrush.transform().dy() ) );
3752  }
3753 
3754  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3755  if ( !mOffset.isNull() )
3756  {
3757  p->translate( -offset );
3758  }
3759 }
3760 
3762 {
3763  prepareExpressions( context );
3764  applyPattern( mBrush, mImageFilePath, mWidth, mAlpha, context );
3765 }
3766 
3768 {
3769  Q_UNUSED( context );
3770 }
3771 
3773 {
3774  QgsStringMap map;
3775  map["imageFile"] = mImageFilePath;
3776  map["coordinate_mode"] = QString::number( mCoordinateMode );
3777  map["alpha"] = QString::number( mAlpha );
3778  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
3779  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
3780  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
3781  map["angle"] = QString::number( mAngle );
3782  map["width"] = QString::number( mWidth );
3783  map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
3784  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
3785 
3787  return map;
3788 }
3789 
3791 {
3794  sl->setAlpha( mAlpha );
3795  sl->setOffset( mOffset );
3796  sl->setOffsetUnit( mOffsetUnit );
3798  sl->setAngle( mAngle );
3799  sl->setWidth( mWidth );
3800  sl->setWidthUnit( mWidthUnit );
3803  copyPaintEffect( sl );
3804  return sl;
3805 }
3806 
3808 {
3809  return mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
3810 }
3811 
3813 {
3814  mImageFilePath = imagePath;
3815 }
3816 
3818 {
3819  mCoordinateMode = mode;
3820 }
3821 
3823 {
3824  mAlpha = alpha;
3825 }
3826 
3828 {
3829  if ( !hasDataDefinedProperties() )
3830  return; // shortcut
3831 
3832  bool hasWidthExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH );
3833  bool hasFileExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILE );
3834  bool hasAlphaExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ALPHA );
3835  bool hasAngleExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE );
3836 
3837  if ( !hasWidthExpression && !hasAngleExpression && !hasAlphaExpression && !hasFileExpression )
3838  {
3839  return; //no data defined settings
3840  }
3841 
3842  bool ok;
3843  if ( hasAngleExpression )
3844  {
3845  context.setOriginalValueVariable( mAngle );
3846  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
3847  if ( ok )
3848  mNextAngle = nextAngle;
3849  }
3850 
3851  if ( !hasWidthExpression && !hasAlphaExpression && !hasFileExpression )
3852  {
3853  return; //nothing further to do
3854  }
3855 
3856  double width = mWidth;
3857  if ( hasWidthExpression )
3858  {
3859  context.setOriginalValueVariable( mWidth );
3861  }
3862  double alpha = mAlpha;
3863  if ( hasAlphaExpression )
3864  {
3865  context.setOriginalValueVariable( mAlpha );
3867  }
3868  QString file = mImageFilePath;
3869  if ( hasFileExpression )
3870  {
3873  }
3874  applyPattern( mBrush, file, width, alpha, context );
3875 }
3876 
3877 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolV2RenderContext &context )
3878 {
3879  QImage image( imageFilePath );
3880  if ( image.isNull() )
3881  {
3882  return;
3883  }
3884  if ( !image.hasAlphaChannel() )
3885  {
3886  image = image.convertToFormat( QImage::Format_ARGB32 );
3887  }
3888 
3889  double pixelWidth;
3890  if ( width > 0 )
3891  {
3893  }
3894  else
3895  {
3896  pixelWidth = image.width();
3897  }
3898 
3899  //reduce alpha of image
3900  if ( alpha < 1.0 )
3901  {
3902  QPainter p;
3903  p.begin( &image );
3904  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3905  QColor alphaColor( 0, 0, 0 );
3906  alphaColor.setAlphaF( alpha );
3907  p.fillRect( image.rect(), alphaColor );
3908  p.end();
3909  }
3910 
3911  //resize image if required
3912  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3913  {
3914  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3915  }
3916 
3917  brush.setTextureImage( image );
3918 }
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
QgsMapUnitScale mSvgOutlineWidthMapUnitScale
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
uchar * scanLine(int i)
static const QString EXPR_DISTANCE_Y
void setBorderWidthMapUnitScale(const QgsMapUnitScale &scale)
QgsMapUnitScale mapUnitScale() const override
QgsSymbolV2 * subSymbol() override
void setForceVectorOutput(bool force)
void setDistanceUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit for the maximum distance to shade inside of the shape from the polygon&#39;s boundary...
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
QImage convertToFormat(Format format, QFlags< Qt::ImageConversionFlag > flags) const
#define DEFAULT_SIMPLEFILL_BORDERCOLOR
QString layerType() const override
Returns a string that represents this layer type.
static const QString EXPR_DISPLACEMENT_Y
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
bool ignoreRings() const
Returns whether the shapeburst fill is set to ignore polygon interior rings.
double rendererScale() const
virtual QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
void setReferencePoint1(QPointF referencePoint)
Starting point of gradient fill, in the range [0,0] - [1,1].
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:391
QgsSymbolV2::OutputUnit mSvgOutlineWidthUnit
void setStyle(Qt::PenStyle style)
void setReferencePoint2IsCentroid(bool isCentroid)
Sets the end point of the gradient to be the feature centroid.
void startRender(QgsSymbolV2RenderContext &context) override
void setSvgOutlineWidth(double w)
QString & append(QChar ch)
static void multiplyImageOpacity(QImage *image, qreal alpha)
Multiplies opacity of image pixel values with a (global) transparency value.
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:65
QImage scaledToWidth(int width, Qt::TransformationMode mode) const
QColor dxfColor(QgsSymbolV2RenderContext &context) const override
get color
QgsSymbolV2::OutputUnit mOffsetUnit
static Qt::BrushStyle decodeBrushStyle(const QString &str)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
FillCoordinateMode mCoordinateMode
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=nullptr, Qt::PenCapStyle *penCapStyle=nullptr, QVector< qreal > *customDashPattern=nullptr, double *dashOffset=nullptr)
QgsSymbolV2::OutputUnit mLineWidthUnit
QByteArray getImageData(const QString &path) const
Get image data.
virtual QString type() const =0
Returns a string representing the color ramp type.
QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
bool end()
bool contains(const Key &key) const
static Q_DECL_DEPRECATED bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &borderColor, double &borderWidth, double &size)
static const QString EXPR_BORDER_COLOR
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
QgsRasterFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
virtual QgsLineSymbolV2 * clone() const override
const uchar * constScanLine(int i) const
qreal alphaF() const
void setSvgFillColor(const QColor &c)
void setPatternWidthMapUnitScale(const QgsMapUnitScale &scale)
void setRenderHint(RenderHint hint, bool on)
void startRender(QgsSymbolV2RenderContext &context) override
QDomNode appendChild(const QDomNode &newChild)
QByteArray toHex() const
void setColor(const QColor &c) override
The fill color.
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setDistanceYUnit(QgsSymbolV2::OutputUnit unit)
void stopRender(QgsSymbolV2RenderContext &context) override
void append(const T &value)
qreal dx() const
qreal dy() const
QString imageFilePath() const
The path to the raster image used for the fill.
static double rescaleUom(double size, QgsSymbolV2::OutputUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
static const QString EXPR_USE_WHOLE_SHAPE
double dxfWidth(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
get line width
QString name() const
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&.
static const QString EXPR_REFERENCE2_Y
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QMap< Key, T > & unite(const QMap< Key, T > &other)
void setOutlineWidthMapUnitScale(const QgsMapUnitScale &scale)
double dxfAngle(QgsSymbolV2RenderContext &context) const override
get angle
virtual QSet< QString > usedAttributes() const
Returns the set of attributes referenced by the layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsPointPatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
GradientCoordinateMode coordinateMode() const
Coordinate mode for gradient.
void setMatrix(const QMatrix &matrix)
GradientType gradientType() const
Type of gradient, eg linear or radial.
bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
void setColorAt(qreal position, const QColor &color)
static QString encodeColor(const QColor &color)
Base class for polygon renderers generating texture images.
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QgsMapUnitScale mPatternWidthMapUnitScale
static const QString EXPR_DISPLACEMENT_X
GradientCoordinateMode mCoordinateMode
void stopRender(QgsSymbolV2RenderContext &context) override
void setPointOnSurface(bool pointOnSurface)
static const QString EXPR_WIDTH
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
static void externalGraphicToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &mime, const QColor &color, double size=-1)
void scale(qreal sx, qreal sy)
void setIgnoreRings(bool ignoreRings)
Sets whether the shapeburst fill should ignore polygon rings when calculating the buffered shading...
void setRendererScale(double scale)
bool isValid() const
void stopRender(QgsSymbolV2RenderContext &context) override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the SLD element following the SLD v1.1 specs.
void setTextureImage(const QImage &image)
QPointF offset() const
Returns the offset for the shapeburst fill.
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setDistanceMapUnitScale(const QgsMapUnitScale &scale)
void stopRender(QgsSymbolV2RenderContext &context) override
bool isEmpty() const
QString layerType() const override
Returns a string that represents this layer type.
virtual bool hasDataDefinedProperties() const
Checks whether the layer has any associated data defined properties.
void save()
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
bool hasAlphaChannel() const
QString layerType() const override
Returns a string that represents this layer type.
QgsMapUnitScale mOutlineWidthMapUnitScale
Qt::BrushStyle dxfBrushStyle() const override
get brush/fill 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=nullptr)
Create ogr feature style string for pen.
QgsSymbolV2::OutputUnit outputUnit() const override
static const QString EXPR_BLUR_RADIUS
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setJoinStyle(Qt::PenJoinStyle style)
QgsShapeburstFillSymbolLayerV2(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, ShapeburstColorType colorType=SimpleTwoColor, int blurRadius=0, bool useWholeShape=true, double maxDistance=5)
virtual QgsStringMap properties() const =0
Returns a string map containing all the color ramp&#39;s properties.
SymbolType type() const
Definition: qgssymbolv2.h:107
static const QString EXPR_COORDINATE_MODE
qreal top() const
Line symbol.
Definition: qgssymbolv2.h:82
QgsSymbolV2::OutputUnit mWidthUnit
static QPointF decodePoint(const QString &str)
QgsSymbolV2::OutputUnit mDisplacementXUnit
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void stopRender(QgsSymbolV2RenderContext &context) override
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static const QString EXPR_COLOR2
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.
void setDistanceXMapUnitScale(const QgsMapUnitScale &scale)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
static const bool selectionIsOpaque
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:385
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:97
void applyDataDefinedSettings(QgsSymbolV2RenderContext &context) override
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
ShapeburstColorType colorType() const
Returns the color mode used for the shapeburst fill.
bool isNull() const
virtual bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
double mDistance
Distance (in mm or map units) between lines.
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
void setSvgOutlineWidthUnit(QgsSymbolV2::OutputUnit unit)
void setPointOnAllParts(bool pointOnAllParts)
Sets whether a point is drawn for all parts or only on the biggest part of multi-part features...
QImage copy(const QRect &rectangle) const
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
double mLineAngle
Vector line angle in degrees (0 = horizontal, counterclockwise)
bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setMapUnitScale(const QgsMapUnitScale &scale) override
double toDouble(bool *ok) const
QColor color() const override
The fill color.
void setStyle(Qt::BrushStyle style)
QgsVectorColorRampV2 * mGradientRamp
QString layerType() const override
Returns a string that represents this layer type.
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QString tr(const char *sourceText, const char *disambiguation, int n)
qreal left() const
void setColorRamp(QgsVectorColorRampV2 *ramp)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
virtual QColor color() const override
The fill color.
QgsSymbolV2::OutputUnit svgOutlineWidthUnit() const
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> pixel dimensions.
void setWidth(double width)
virtual Q_DECL_DEPRECATED QVariant evaluateDataDefinedProperty(const QString &property, const QgsFeature *feature, const QVariant &defaultVal=QVariant(), bool *ok=nullptr) const
Evaluates the matching data defined property and returns the calculated value.
Marker symbol.
Definition: qgssymbolv2.h:81
void setMapUnitScale(const QgsMapUnitScale &scale)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the SLD element following the SLD v1.1 specs.
QgsSymbolV2::OutputUnit outputUnit() const override
void setSvgOutlineColor(const QColor &c)
double symbologyScaleDenominator() const
Retrieve reference scale for output.
Definition: qgsdxfexport.h:84
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setInterval(double interval)
The interval between individual markers.
static const QString EXPR_JOIN_STYLE
void setDistanceUnit(QgsSymbolV2::OutputUnit unit)
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
int width() const
QColor dxfColor(QgsSymbolV2RenderContext &context) const override
get color
static const QString EXPR_FILL_STYLE
static QgsSvgCache * instance()
Definition: qgssvgcache.cpp:97
static const QString EXPR_REFERENCE1_Y
static QString encodePenStyle(Qt::PenStyle style)
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setCapStyle(Qt::PenCapStyle style)
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.
QString mImageFilePath
Path to the image file.
QgsSymbolV2::OutputUnit mDisplacementYUnit
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
static const QString EXPR_BORDER_STYLE
void setColor(const QColor &color)
static const QString EXPR_REFERENCE2_X
static const QString EXPR_REFERENCE2_ISCENTROID
static QString symbolPathToName(QString path)
Get symbols&#39;s name from its path.
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
GradientSpread gradientSpread() const
Gradient spread mode.
Mixed units in symbol layers.
Definition: qgssymbolv2.h:69
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
static const QString EXPR_LINEWIDTH
QgsLinePatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSymbolV2::OutputUnit outputUnit() const override
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
QTransform & translate(qreal dx, qreal dy)
The output shall be in millimeters.
Definition: qgssymbolv2.h:67
#define DEFAULT_SIMPLEFILL_BORDERSTYLE
void stopRender(QgsSymbolV2RenderContext &context) override
QString number(int n, int base)
A class for filling symbols with a repeated raster image.
void setOffset(QPointF offset)
Sets the offset for the shapeburst fill.
QByteArray mSvgData
SVG data.
qreal x() const
qreal y() const
const QgsAbstractGeometryV2 * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
void append(const T &value)
void setOutlineWidthUnit(QgsSymbolV2::OutputUnit unit)
QgsLineSymbolV2 * mOutline
Custom outline.
static const QString EXPR_SPREAD
#define DEFAULT_SIMPLEFILL_STYLE
void startRender(QgsSymbolV2RenderContext &context) override
QPointF p2() const
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
void setScaleFactor(double factor)
QgsGradientFillSymbolLayerV2 * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
virtual QColor fillColor() const
Get fill color.
void setTransform(const QTransform &matrix)
QgsSymbolV2::OutputUnit patternWidthUnit() const
QTransform & scale(qreal sx, qreal sy)
QString layerType() const override
Returns a string that represents this layer type.
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
static const QString EXPR_LINEANGLE
int toInt(bool *ok) const
void setWidth(const double width)
Sets the width for scaling the image used in the fill.
void startRender(QgsSymbolV2RenderContext &context) override
void applyDataDefinedSettings(QgsSymbolV2RenderContext &context) override