QGIS API Documentation  2.17.0-Master (8784312)
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  }
357 
358  // <se:Displacement>
360 }
361 
362 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
363 {
364  //brush
365  QString symbolStyle;
367  symbolStyle.append( ';' );
368  //pen
369  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor, mPenJoinStyle ) );
370  return symbolStyle;
371 }
372 
374 {
375  QgsDebugMsg( "Entered." );
376 
378  Qt::BrushStyle fillStyle;
379  Qt::PenStyle borderStyle;
380  double borderWidth;
381 
382  QDomElement fillElem = element.firstChildElement( "Fill" );
383  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
384 
385  QDomElement strokeElem = element.firstChildElement( "Stroke" );
386  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
387 
388  QPointF offset;
390 
391  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
392  sl->setOffset( offset );
393  return sl;
394 }
395 
397 {
398  double penBleed = mBorderStyle == Qt::NoPen ? 0 : ( mBorderWidth / 2.0 );
399  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
400  return penBleed + offsetBleed;
401 }
402 
404 {
405  double width = mBorderWidth;
407  {
410  }
412 }
413 
415 {
417  {
418  bool ok;
421  if ( ok )
422  return QgsSymbolLayerV2Utils::decodeColor( color );
423  }
424  return mBorderColor;
425 }
426 
428 {
429  double angle = mAngle;
431  {
432  context.setOriginalValueVariable( mAngle );
434  }
435  return angle;
436 }
437 
439 {
440  return mBorderStyle;
441 }
442 
444 {
446  {
447  bool ok;
450  if ( ok )
451  return QgsSymbolLayerV2Utils::decodeColor( color );
452  }
453  return mColor;
454 }
455 
457 {
458  return mBrushStyle;
459 }
460 
461 //QgsGradientFillSymbolLayer
462 
464  GradientColorType colorType, GradientType gradientType,
465  GradientCoordinateMode coordinateMode, GradientSpread spread )
466  : mGradientColorType( colorType )
467  , mGradientRamp( nullptr )
468  , mGradientType( gradientType )
469  , mCoordinateMode( coordinateMode )
470  , mGradientSpread( spread )
471  , mReferencePoint1( QPointF( 0.5, 0 ) )
472  , mReferencePoint1IsCentroid( false )
473  , mReferencePoint2( QPointF( 0.5, 1 ) )
474  , mReferencePoint2IsCentroid( false )
475  , mOffsetUnit( QgsSymbolV2::MM )
476 {
477  mColor = color;
478  mColor2 = color2;
479 }
480 
482 {
483  delete mGradientRamp;
484 }
485 
487 {
488  //default to a two-color, linear gradient with feature mode and pad spreading
493  //default to gradient from the default fill color to white
495  QPointF referencePoint1 = QPointF( 0.5, 0 );
496  bool refPoint1IsCentroid = false;
497  QPointF referencePoint2 = QPointF( 0.5, 1 );
498  bool refPoint2IsCentroid = false;
499  double angle = 0;
500  QPointF offset;
501 
502  //update gradient properties from props
503  if ( props.contains( "type" ) )
504  type = static_cast< GradientType >( props["type"].toInt() );
505  if ( props.contains( "coordinate_mode" ) )
506  coordinateMode = static_cast< GradientCoordinateMode >( props["coordinate_mode"].toInt() );
507  if ( props.contains( "spread" ) )
508  gradientSpread = static_cast< GradientSpread >( props["spread"].toInt() );
509  if ( props.contains( "color_type" ) )
510  colorType = static_cast< GradientColorType >( props["color_type"].toInt() );
511  if ( props.contains( "gradient_color" ) )
512  {
513  //pre 2.5 projects used "gradient_color"
514  color = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color"] );
515  }
516  else if ( props.contains( "color" ) )
517  {
518  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
519  }
520  if ( props.contains( "gradient_color2" ) )
521  {
522  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
523  }
524 
525  if ( props.contains( "reference_point1" ) )
526  referencePoint1 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point1"] );
527  if ( props.contains( "reference_point1_iscentroid" ) )
528  refPoint1IsCentroid = props["reference_point1_iscentroid"].toInt();
529  if ( props.contains( "reference_point2" ) )
530  referencePoint2 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point2"] );
531  if ( props.contains( "reference_point2_iscentroid" ) )
532  refPoint2IsCentroid = props["reference_point2_iscentroid"].toInt();
533  if ( props.contains( "angle" ) )
534  angle = props["angle"].toDouble();
535 
536  if ( props.contains( "offset" ) )
537  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
538 
539  //attempt to create color ramp from props
541 
542  //create a new gradient fill layer with desired properties
543  QgsGradientFillSymbolLayerV2* sl = new QgsGradientFillSymbolLayerV2( color, color2, colorType, type, coordinateMode, gradientSpread );
544  sl->setOffset( offset );
545  if ( props.contains( "offset_unit" ) )
546  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
547  if ( props.contains( "offset_map_unit_scale" ) )
548  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
549  sl->setReferencePoint1( referencePoint1 );
550  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
551  sl->setReferencePoint2( referencePoint2 );
552  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
553  sl->setAngle( angle );
554  if ( gradientRamp )
555  sl->setColorRamp( gradientRamp );
556 
557  sl->restoreDataDefinedProperties( props );
558 
559  return sl;
560 }
561 
563 {
564  delete mGradientRamp;
565  mGradientRamp = ramp;
566 }
567 
569 {
570  return "GradientFill";
571 }
572 
573 void QgsGradientFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, const QPolygonF& points )
574 {
576  {
577  //shortcut
580  return;
581  }
582 
583  bool ok;
584 
585  //first gradient color
586  QColor color = mColor;
588  {
591  if ( ok )
592  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
593  }
594 
595  //second gradient color
598  {
601  if ( ok )
602  color2 = QgsSymbolLayerV2Utils::decodeColor( colorString );
603  }
604 
605  //gradient rotation angle
606  double angle = mAngle;
608  {
609  context.setOriginalValueVariable( mAngle );
611  }
612 
613  //gradient type
616  {
618  if ( ok )
619  {
620  if ( currentType == QObject::tr( "linear" ) )
621  {
623  }
624  else if ( currentType == QObject::tr( "radial" ) )
625  {
627  }
628  else if ( currentType == QObject::tr( "conical" ) )
629  {
631  }
632  }
633  }
634 
635  //coordinate mode
638  {
640  if ( ok )
641  {
642  if ( currentCoordMode == QObject::tr( "feature" ) )
643  {
644  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
645  }
646  else if ( currentCoordMode == QObject::tr( "viewport" ) )
647  {
649  }
650  }
651  }
652 
653  //gradient spread
656  {
658  if ( ok )
659  {
660  if ( currentSpread == QObject::tr( "pad" ) )
661  {
663  }
664  else if ( currentSpread == QObject::tr( "repeat" ) )
665  {
667  }
668  else if ( currentSpread == QObject::tr( "reflect" ) )
669  {
671  }
672  }
673  }
674 
675  //reference point 1 x & y
676  double refPoint1X = mReferencePoint1.x();
678  {
679  context.setOriginalValueVariable( refPoint1X );
680  refPoint1X = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_X, context, refPoint1X ).toDouble();
681  }
682  double refPoint1Y = mReferencePoint1.y();
684  {
685  context.setOriginalValueVariable( refPoint1Y );
686  refPoint1Y = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_Y, context, refPoint1Y ).toDouble();
687  }
688  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
690  {
691  context.setOriginalValueVariable( refPoint1IsCentroid );
692  refPoint1IsCentroid = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE1_ISCENTROID, context, refPoint1IsCentroid ).toBool();
693  }
694 
695  //reference point 2 x & y
696  double refPoint2X = mReferencePoint2.x();
698  {
699  context.setOriginalValueVariable( refPoint2X );
700  refPoint2X = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_X, context, refPoint2X ).toDouble();
701  }
702  double refPoint2Y = mReferencePoint2.y();
704  {
705  context.setOriginalValueVariable( refPoint2Y );
706  refPoint2Y = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_Y, context, refPoint2Y ).toDouble();
707  }
708  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
710  {
711  context.setOriginalValueVariable( refPoint2IsCentroid );
712  refPoint2IsCentroid = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_REFERENCE2_ISCENTROID, context, refPoint2IsCentroid ).toBool();
713  }
714 
715  if ( refPoint1IsCentroid || refPoint2IsCentroid )
716  {
717  //either the gradient is starting or ending at a centroid, so calculate it
718  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
719  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
720  QRectF bbox = points.boundingRect();
721  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
722  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
723 
724  if ( refPoint1IsCentroid )
725  {
726  refPoint1X = centroidX;
727  refPoint1Y = centroidY;
728  }
729  if ( refPoint2IsCentroid )
730  {
731  refPoint2X = centroidX;
732  refPoint2Y = centroidY;
733  }
734  }
735 
736  //update gradient with data defined values
737  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
738  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
739 }
740 
741 QPointF QgsGradientFillSymbolLayerV2::rotateReferencePoint( QPointF refPoint, double angle )
742 {
743  //rotate a reference point by a specified angle around the point (0.5, 0.5)
744 
745  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
746  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
747  //rotate this line by the current rotation angle
748  refLine.setAngle( refLine.angle() + angle );
749  //get new end point of line
750  QPointF rotatedReferencePoint = refLine.p2();
751  //make sure coords of new end point is within [0, 1]
752  if ( rotatedReferencePoint.x() > 1 )
753  rotatedReferencePoint.setX( 1 );
754  if ( rotatedReferencePoint.x() < 0 )
755  rotatedReferencePoint.setX( 0 );
756  if ( rotatedReferencePoint.y() > 1 )
757  rotatedReferencePoint.setY( 1 );
758  if ( rotatedReferencePoint.y() < 0 )
759  rotatedReferencePoint.setY( 0 );
760 
761  return rotatedReferencePoint;
762 }
763 
764 void QgsGradientFillSymbolLayerV2::applyGradient( const QgsSymbolV2RenderContext &context, QBrush &brush,
769 {
770  //update alpha of gradient colors
772  fillColor.setAlphaF( context.alpha() * fillColor.alphaF() );
773  QColor fillColor2 = color2;
774  fillColor2.setAlphaF( context.alpha() * fillColor2.alphaF() );
775 
776  //rotate reference points
777  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
778  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
779 
780  //create a QGradient with the desired properties
781  QGradient gradient;
782  switch ( gradientType )
783  {
785  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
786  break;
788  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
789  break;
791  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
792  break;
793  }
794  switch ( coordinateMode )
795  {
797  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
798  break;
800  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
801  break;
802  }
803  switch ( gradientSpread )
804  {
806  gradient.setSpread( QGradient::PadSpread );
807  break;
809  gradient.setSpread( QGradient::ReflectSpread );
810  break;
812  gradient.setSpread( QGradient::RepeatSpread );
813  break;
814  }
815 
816  //add stops to gradient
817  if ( gradientColorType == QgsGradientFillSymbolLayerV2::ColorRamp && gradientRamp && gradientRamp->type() == "gradient" )
818  {
819  //color ramp gradient
820  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( gradientRamp );
821  gradRamp->addStopsToGradient( &gradient, context.alpha() );
822  }
823  else
824  {
825  //two color gradient
826  gradient.setColorAt( 0.0, fillColor );
827  gradient.setColorAt( 1.0, fillColor2 );
828  }
829 
830  //update QBrush use gradient
831  brush = QBrush( gradient );
832 }
833 
835 {
836  QColor selColor = context.renderContext().selectionColor();
837  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
838  mSelBrush = QBrush( selColor );
839 
840  //update mBrush to use a gradient fill with specified properties
841  prepareExpressions( context );
842 }
843 
845 {
846  Q_UNUSED( context );
847 }
848 
850 {
851  QPainter* p = context.renderContext().painter();
852  if ( !p )
853  {
854  return;
855  }
856 
857  applyDataDefinedSymbology( context, points );
858 
859  p->setBrush( context.selected() ? mSelBrush : mBrush );
860  p->setPen( Qt::NoPen );
861 
862  QPointF offset;
863  if ( !mOffset.isNull() )
864  {
867  p->translate( offset );
868  }
869 
870  _renderPolygon( p, points, rings, context );
871 
872  if ( !mOffset.isNull() )
873  {
874  p->translate( -offset );
875  }
876 }
877 
879 {
880  QgsStringMap map;
881  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
882  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
883  map["color_type"] = QString::number( mGradientColorType );
884  map["type"] = QString::number( mGradientType );
885  map["coordinate_mode"] = QString::number( mCoordinateMode );
886  map["spread"] = QString::number( mGradientSpread );
887  map["reference_point1"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint1 );
888  map["reference_point1_iscentroid"] = QString::number( mReferencePoint1IsCentroid );
889  map["reference_point2"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint2 );
890  map["reference_point2_iscentroid"] = QString::number( mReferencePoint2IsCentroid );
891  map["angle"] = QString::number( mAngle );
892  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
894  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
896  if ( mGradientRamp )
897  {
898  map.unite( mGradientRamp->properties() );
899  }
900  return map;
901 }
902 
904 {
906  if ( mGradientRamp )
907  sl->setColorRamp( mGradientRamp->clone() );
912  sl->setAngle( mAngle );
913  sl->setOffset( mOffset );
914  sl->setOffsetUnit( mOffsetUnit );
917  copyPaintEffect( sl );
918  return sl;
919 }
920 
922 {
923  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
924  return offsetBleed;
925 }
926 
928 {
929  mOffsetUnit = unit;
930 }
931 
933 {
934  return mOffsetUnit;
935 }
936 
938 {
939  mOffsetMapUnitScale = scale;
940 }
941 
943 {
944  return mOffsetMapUnitScale;
945 }
946 
947 //QgsShapeburstFillSymbolLayer
948 
950  int blurRadius, bool useWholeShape, double maxDistance )
951  : mBlurRadius( blurRadius )
952  , mUseWholeShape( useWholeShape )
953  , mMaxDistance( maxDistance )
954  , mDistanceUnit( QgsSymbolV2::MM )
955  , mColorType( colorType )
956  , mColor2( color2 )
957  , mGradientRamp( nullptr )
958  , mTwoColorGradientRamp( nullptr )
959  , mIgnoreRings( false )
960  , mOffsetUnit( QgsSymbolV2::MM )
961 {
962  mColor = color;
963 }
964 
966 {
967  delete mGradientRamp;
968 }
969 
971 {
972  //default to a two-color gradient
974  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
975  int blurRadius = 0;
976  bool useWholeShape = true;
977  double maxDistance = 5;
978  QPointF offset;
979 
980  //update fill properties from props
981  if ( props.contains( "color_type" ) )
982  {
983  colorType = static_cast< ShapeburstColorType >( props["color_type"].toInt() );
984  }
985  if ( props.contains( "shapeburst_color" ) )
986  {
987  //pre 2.5 projects used "shapeburst_color"
988  color = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color"] );
989  }
990  else if ( props.contains( "color" ) )
991  {
992  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
993  }
994 
995  if ( props.contains( "shapeburst_color2" ) )
996  {
997  //pre 2.5 projects used "shapeburst_color2"
998  color2 = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color2"] );
999  }
1000  else if ( props.contains( "gradient_color2" ) )
1001  {
1002  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
1003  }
1004  if ( props.contains( "blur_radius" ) )
1005  {
1006  blurRadius = props["blur_radius"].toInt();
1007  }
1008  if ( props.contains( "use_whole_shape" ) )
1009  {
1010  useWholeShape = props["use_whole_shape"].toInt();
1011  }
1012  if ( props.contains( "max_distance" ) )
1013  {
1014  maxDistance = props["max_distance"].toDouble();
1015  }
1016  if ( props.contains( "offset" ) )
1017  {
1018  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
1019  }
1020 
1021  //attempt to create color ramp from props
1023 
1024  //create a new shapeburst fill layer with desired properties
1025  QgsShapeburstFillSymbolLayerV2* sl = new QgsShapeburstFillSymbolLayerV2( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1026  sl->setOffset( offset );
1027  if ( props.contains( "offset_unit" ) )
1028  {
1029  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
1030  }
1031  if ( props.contains( "distance_unit" ) )
1032  {
1033  sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
1034  }
1035  if ( props.contains( "offset_map_unit_scale" ) )
1036  {
1037  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
1038  }
1039  if ( props.contains( "distance_map_unit_scale" ) )
1040  {
1041  sl->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["distance_map_unit_scale"] ) );
1042  }
1043  if ( props.contains( "ignore_rings" ) )
1044  {
1045  sl->setIgnoreRings( props["ignore_rings"].toInt() );
1046  }
1047  if ( gradientRamp )
1048  {
1049  sl->setColorRamp( gradientRamp );
1050  }
1051 
1052  sl->restoreDataDefinedProperties( props );
1053 
1054  return sl;
1055 }
1056 
1058 {
1059  return "ShapeburstFill";
1060 }
1061 
1063 {
1064  delete mGradientRamp;
1065  mGradientRamp = ramp;
1066 }
1067 
1068 void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
1069  double& maxDistance, bool& ignoreRings )
1070 {
1071  bool ok;
1072 
1073  //first gradient color
1074  color = mColor;
1076  {
1079  if ( ok )
1080  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
1081  }
1082 
1083  //second gradient color
1084  color2 = mColor2;
1086  {
1089  if ( ok )
1090  color2 = QgsSymbolLayerV2Utils::decodeColor( colorString );
1091  }
1092 
1093  //blur radius
1096  {
1099  }
1100 
1101  //use whole shape
1104  {
1107  }
1108 
1109  //max distance
1112  {
1115  }
1116 
1117  //ignore rings
1118  ignoreRings = mIgnoreRings;
1120  {
1123  }
1124 
1125 }
1126 
1128 {
1129  //TODO - check this
1130  QColor selColor = context.renderContext().selectionColor();
1131  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
1132  mSelBrush = QBrush( selColor );
1133 
1134  prepareExpressions( context );
1135 }
1136 
1138 {
1139  Q_UNUSED( context );
1140 }
1141 
1143 {
1144  QPainter* p = context.renderContext().painter();
1145  if ( !p )
1146  {
1147  return;
1148  }
1149 
1150  if ( context.selected() )
1151  {
1152  //feature is selected, draw using selection style
1153  p->setBrush( mSelBrush );
1154  QPointF offset;
1155  if ( !mOffset.isNull() )
1156  {
1159  p->translate( offset );
1160  }
1161  _renderPolygon( p, points, rings, context );
1162  if ( !mOffset.isNull() )
1163  {
1164  p->translate( -offset );
1165  }
1166  return;
1167  }
1168 
1169  QColor color1, color2;
1170  int blurRadius;
1171  bool useWholeShape;
1172  double maxDistance;
1173  bool ignoreRings;
1174  //calculate data defined symbology
1175  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1176 
1177  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1178  int outputPixelMaxDist = 0;
1179  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1180  {
1181  //convert max distance to pixels
1182  const QgsRenderContext& ctx = context.renderContext();
1183  outputPixelMaxDist = maxDistance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit, mDistanceMapUnitScale );
1184  }
1185 
1186  //if we are using the two color mode, create a gradient ramp
1188  {
1189  mTwoColorGradientRamp = new QgsVectorGradientColorRampV2( color1, color2 );
1190  }
1191 
1192  //no border for shapeburst fills
1193  p->setPen( QPen( Qt::NoPen ) );
1194 
1195  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1196  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1197  //create a QImage to draw shapeburst in
1198  double imWidth = points.boundingRect().width() + ( sideBuffer * 2 );
1199  double imHeight = points.boundingRect().height() + ( sideBuffer * 2 );
1200  QImage * fillImage = new QImage( imWidth * context.renderContext().rasterScaleFactor(),
1201  imHeight * context.renderContext().rasterScaleFactor(), QImage::Format_ARGB32_Premultiplied );
1202  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1203  //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
1204  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1205  fillImage->fill( Qt::black );
1206 
1207  //also create an image to store the alpha channel
1208  QImage * alphaImage = new QImage( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1209  //initially fill the alpha channel image with a transparent color
1210  alphaImage->fill( Qt::transparent );
1211 
1212  //now, draw the polygon in the alpha channel image
1213  QPainter imgPainter;
1214  imgPainter.begin( alphaImage );
1215  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1216  imgPainter.setBrush( QBrush( Qt::white ) );
1217  imgPainter.setPen( QPen( Qt::black ) );
1218  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1219  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1220  _renderPolygon( &imgPainter, points, rings, context );
1221  imgPainter.end();
1222 
1223  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1224  //(this avoids calling _renderPolygon twice, since that can be slow)
1225  imgPainter.begin( fillImage );
1226  if ( !ignoreRings )
1227  {
1228  imgPainter.drawImage( 0, 0, *alphaImage );
1229  }
1230  else
1231  {
1232  //using ignore rings mode, so the alpha image can't be used
1233  //directly as the alpha channel contains polygon rings and we need
1234  //to draw now without any rings
1235  imgPainter.setBrush( QBrush( Qt::white ) );
1236  imgPainter.setPen( QPen( Qt::black ) );
1237  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1238  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1239  _renderPolygon( &imgPainter, points, nullptr, context );
1240  }
1241  imgPainter.end();
1242 
1243  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1244  double * dtArray = distanceTransform( fillImage );
1245 
1246  //copy distance transform values back to QImage, shading by appropriate color ramp
1248  context.alpha(), useWholeShape, outputPixelMaxDist );
1249 
1250  //clean up some variables
1251  delete [] dtArray;
1253  {
1254  delete mTwoColorGradientRamp;
1255  }
1256 
1257  //apply blur if desired
1258  if ( blurRadius > 0 )
1259  {
1260  QgsSymbolLayerV2Utils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1261  }
1262 
1263  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1264  imgPainter.begin( fillImage );
1265  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1266  imgPainter.drawImage( 0, 0, *alphaImage );
1267  imgPainter.end();
1268  //we're finished with the alpha channel image now
1269  delete alphaImage;
1270 
1271  //draw shapeburst image in correct place in the destination painter
1272 
1273  p->save();
1274  QPointF offset;
1275  if ( !mOffset.isNull() )
1276  {
1279  p->translate( offset );
1280  }
1281 
1282  p->scale( 1 / context.renderContext().rasterScaleFactor(), 1 / context.renderContext().rasterScaleFactor() );
1283  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1284 
1285  delete fillImage;
1286 
1287  if ( !mOffset.isNull() )
1288  {
1289  p->translate( -offset );
1290  }
1291  p->restore();
1292 
1293 }
1294 
1295 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1296 
1297 /* distance transform of a 1d function using squared distance */
1298 void QgsShapeburstFillSymbolLayerV2::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1299 {
1300  int k = 0;
1301  v[0] = 0;
1302  z[0] = -INF;
1303  z[1] = + INF;
1304  for ( int q = 1; q <= n - 1; q++ )
1305  {
1306  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1307  while ( s <= z[k] )
1308  {
1309  k--;
1310  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1311  }
1312  k++;
1313  v[k] = q;
1314  z[k] = s;
1315  z[k+1] = + INF;
1316  }
1317 
1318  k = 0;
1319  for ( int q = 0; q <= n - 1; q++ )
1320  {
1321  while ( z[k+1] < q )
1322  k++;
1323  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1324  }
1325 }
1326 
1327 /* distance transform of 2d function using squared distance */
1328 void QgsShapeburstFillSymbolLayerV2::distanceTransform2d( double * im, int width, int height )
1329 {
1330  int maxDimension = qMax( width, height );
1331  double *f = new double[ maxDimension ];
1332  int *v = new int[ maxDimension ];
1333  double *z = new double[ maxDimension + 1 ];
1334  double *d = new double[ maxDimension ];
1335 
1336  // transform along columns
1337  for ( int x = 0; x < width; x++ )
1338  {
1339  for ( int y = 0; y < height; y++ )
1340  {
1341  f[y] = im[ x + y * width ];
1342  }
1343  distanceTransform1d( f, height, v, z, d );
1344  for ( int y = 0; y < height; y++ )
1345  {
1346  im[ x + y * width ] = d[y];
1347  }
1348  }
1349 
1350  // transform along rows
1351  for ( int y = 0; y < height; y++ )
1352  {
1353  for ( int x = 0; x < width; x++ )
1354  {
1355  f[x] = im[ x + y*width ];
1356  }
1357  distanceTransform1d( f, width, v, z, d );
1358  for ( int x = 0; x < width; x++ )
1359  {
1360  im[ x + y*width ] = d[x];
1361  }
1362  }
1363 
1364  delete [] d;
1365  delete [] f;
1366  delete [] v;
1367  delete [] z;
1368 }
1369 
1370 /* distance transform of a binary QImage */
1371 double * QgsShapeburstFillSymbolLayerV2::distanceTransform( QImage *im )
1372 {
1373  int width = im->width();
1374  int height = im->height();
1375 
1376  double * dtArray = new double[width * height];
1377 
1378  //load qImage to array
1379  QRgb tmpRgb;
1380  int idx = 0;
1381  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1382  {
1383  const QRgb* scanLine = reinterpret_cast< const QRgb* >( im->constScanLine( heightIndex ) );
1384  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1385  {
1386  tmpRgb = scanLine[widthIndex];
1387  if ( qRed( tmpRgb ) == 0 )
1388  {
1389  //black pixel, so zero distance
1390  dtArray[ idx ] = 0;
1391  }
1392  else
1393  {
1394  //white pixel, so initially set distance as infinite
1395  dtArray[ idx ] = INF;
1396  }
1397  idx++;
1398  }
1399  }
1400 
1401  //calculate squared distance transform
1402  distanceTransform2d( dtArray, width, height );
1403 
1404  return dtArray;
1405 }
1406 
1407 void QgsShapeburstFillSymbolLayerV2::dtArrayToQImage( double * array, QImage *im, QgsVectorColorRampV2* ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1408 {
1409  int width = im->width();
1410  int height = im->height();
1411 
1412  //find maximum distance value
1413  double maxDistanceValue;
1414 
1415  if ( useWholeShape )
1416  {
1417  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1418  double dtMaxValue = array[0];
1419  for ( int i = 1; i < ( width * height ); ++i )
1420  {
1421  if ( array[i] > dtMaxValue )
1422  {
1423  dtMaxValue = array[i];
1424  }
1425  }
1426 
1427  //values in distance transform are squared
1428  maxDistanceValue = sqrt( dtMaxValue );
1429  }
1430  else
1431  {
1432  //use max distance set in symbol properties
1433  maxDistanceValue = maxPixelDistance;
1434  }
1435 
1436  //update the pixels in the provided QImage
1437  int idx = 0;
1438  double squaredVal = 0;
1439  double pixVal = 0;
1440  QColor pixColor;
1441  bool layerHasAlpha = layerAlpha < 1.0;
1442 
1443  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1444  {
1445  QRgb* scanLine = reinterpret_cast< QRgb* >( im->scanLine( heightIndex ) );
1446  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1447  {
1448  //result of distance transform
1449  squaredVal = array[idx];
1450 
1451  //scale result to fit in the range [0, 1]
1452  if ( maxDistanceValue > 0 )
1453  {
1454  pixVal = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1455  }
1456  else
1457  {
1458  pixVal = 1.0;
1459  }
1460 
1461  //convert value to color from ramp
1462  pixColor = ramp->color( pixVal );
1463 
1464  int pixAlpha = pixColor.alpha();
1465  if (( layerHasAlpha ) || ( pixAlpha != 255 ) )
1466  {
1467  //apply layer's transparency to alpha value
1468  double alpha = pixAlpha * layerAlpha;
1469  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1470  QgsSymbolLayerV2Utils::premultiplyColor( pixColor, alpha );
1471  }
1472 
1473  scanLine[widthIndex] = pixColor.rgba();
1474  idx++;
1475  }
1476  }
1477 }
1478 
1480 {
1481  QgsStringMap map;
1482  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1483  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
1484  map["color_type"] = QString::number( mColorType );
1485  map["blur_radius"] = QString::number( mBlurRadius );
1486  map["use_whole_shape"] = QString::number( mUseWholeShape );
1487  map["max_distance"] = QString::number( mMaxDistance );
1488  map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1489  map["distance_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale );
1490  map["ignore_rings"] = QString::number( mIgnoreRings );
1491  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1492  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1493  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1494 
1496 
1497  if ( mGradientRamp )
1498  {
1499  map.unite( mGradientRamp->properties() );
1500  }
1501 
1502  return map;
1503 }
1504 
1506 {
1508  if ( mGradientRamp )
1509  {
1510  sl->setColorRamp( mGradientRamp->clone() );
1511  }
1515  sl->setOffset( mOffset );
1516  sl->setOffsetUnit( mOffsetUnit );
1519  copyPaintEffect( sl );
1520  return sl;
1521 }
1522 
1524 {
1525  double offsetBleed = qMax( mOffset.x(), mOffset.y() );
1526  return offsetBleed;
1527 }
1528 
1530 {
1531  mDistanceUnit = unit;
1532  mOffsetUnit = unit;
1533 }
1534 
1536 {
1537  if ( mDistanceUnit == mOffsetUnit )
1538  {
1539  return mDistanceUnit;
1540  }
1541  return QgsSymbolV2::Mixed;
1542 }
1543 
1545 {
1546  mDistanceMapUnitScale = scale;
1547  mOffsetMapUnitScale = scale;
1548 }
1549 
1551 {
1553  {
1554  return mDistanceMapUnitScale;
1555  }
1556  return QgsMapUnitScale();
1557 }
1558 
1559 
1560 //QgsImageFillSymbolLayer
1561 
1563  : mNextAngle( 0.0 )
1564  , mOutlineWidth( 0.0 )
1565  , mOutlineWidthUnit( QgsSymbolV2::MM )
1566  , mOutline( nullptr )
1567 {
1568  setSubSymbol( new QgsLineSymbolV2() );
1569 }
1570 
1572 {
1573 }
1574 
1576 {
1577  QPainter* p = context.renderContext().painter();
1578  if ( !p )
1579  {
1580  return;
1581  }
1582 
1583  mNextAngle = mAngle;
1584  applyDataDefinedSettings( context );
1585 
1586  p->setPen( QPen( Qt::NoPen ) );
1587 
1588  QTransform bkTransform = mBrush.transform();
1590  {
1591  //transform brush to upper left corner of geometry bbox
1592  QPointF leftCorner = points.boundingRect().topLeft();
1593  QTransform t = mBrush.transform();
1594  t.translate( leftCorner.x(), leftCorner.y() );
1595  mBrush.setTransform( t );
1596  }
1597 
1598  if ( context.selected() )
1599  {
1600  QColor selColor = context.renderContext().selectionColor();
1601  // Alister - this doesn't seem to work here
1602  //if ( ! selectionIsOpaque )
1603  // selColor.setAlphaF( context.alpha() );
1604  p->setBrush( QBrush( selColor ) );
1605  _renderPolygon( p, points, rings, context );
1606  }
1607 
1608  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1609  {
1610  QTransform t = mBrush.transform();
1611  t.rotate( mNextAngle );
1612  mBrush.setTransform( t );
1613  }
1614  p->setBrush( mBrush );
1615  _renderPolygon( p, points, rings, context );
1616  if ( mOutline )
1617  {
1618  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1619  if ( rings )
1620  {
1621  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1622  for ( ; ringIt != rings->constEnd(); ++ringIt )
1623  {
1624  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1625  }
1626  }
1627  }
1628 
1629  mBrush.setTransform( bkTransform );
1630 }
1631 
1633 {
1634  if ( !symbol ) //unset current outline
1635  {
1636  delete mOutline;
1637  mOutline = nullptr;
1638  return true;
1639  }
1640 
1641  if ( symbol->type() != QgsSymbolV2::Line )
1642  {
1643  delete symbol;
1644  return false;
1645  }
1646 
1647  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1648  if ( lineSymbol )
1649  {
1650  delete mOutline;
1651  mOutline = lineSymbol;
1652  return true;
1653  }
1654 
1655  delete symbol;
1656  return false;
1657 }
1658 
1660 {
1661  mOutlineWidthUnit = unit;
1662 }
1663 
1665 {
1666  return mOutlineWidthUnit;
1667 }
1668 
1670 {
1671  mOutlineWidthMapUnitScale = scale;
1672 }
1673 
1675 {
1677 }
1678 
1680 {
1681  if ( mOutline && mOutline->symbolLayer( 0 ) )
1682  {
1683  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
1684  return subLayerBleed;
1685  }
1686  return 0;
1687 }
1688 
1690 {
1691  double width = mOutlineWidth;
1693  {
1696  }
1698 }
1699 
1701 {
1702  Q_UNUSED( context );
1703  if ( !mOutline )
1704  {
1705  return QColor( Qt::black );
1706  }
1707  return mOutline->color();
1708 }
1709 
1711 {
1712  return Qt::SolidLine;
1713 #if 0
1714  if ( !mOutline )
1715  {
1716  return Qt::SolidLine;
1717  }
1718  else
1719  {
1720  return mOutline->dxfPenStyle();
1721  }
1722 #endif //0
1723 }
1724 
1726 {
1728  if ( mOutline )
1729  attr.unite( mOutline->usedAttributes() );
1730  return attr;
1731 }
1732 
1733 
1734 //QgsSVGFillSymbolLayer
1735 
1736 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle )
1738  , mPatternWidth( width )
1739  , mPatternWidthUnit( QgsSymbolV2::MM )
1740  , mSvgOutlineWidthUnit( QgsSymbolV2::MM )
1741 {
1742  setSvgFilePath( svgFilePath );
1743  mOutlineWidth = 0.3;
1744  mAngle = angle;
1745  mColor = QColor( 255, 255, 255 );
1746  mSvgOutlineColor = QColor( 0, 0, 0 );
1747  mSvgOutlineWidth = 0.2;
1748  setDefaultSvgParams();
1749  mSvgPattern = nullptr;
1750 }
1751 
1752 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle )
1754  , mPatternWidth( width )
1756  , mSvgData( svgData )
1758 {
1759  storeViewBox();
1760  mOutlineWidth = 0.3;
1761  mAngle = angle;
1762  mColor = QColor( 255, 255, 255 );
1763  mSvgOutlineColor = QColor( 0, 0, 0 );
1764  mSvgOutlineWidth = 0.2;
1765  setSubSymbol( new QgsLineSymbolV2() );
1766  setDefaultSvgParams();
1767  mSvgPattern = nullptr;
1768 }
1769 
1771 {
1772  delete mSvgPattern;
1773 }
1774 
1776 {
1778  mPatternWidthUnit = unit;
1779  mSvgOutlineWidthUnit = unit;
1780  mOutlineWidthUnit = unit;
1781 }
1782 
1784 {
1786  if ( mPatternWidthUnit != unit || mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
1787  {
1788  return QgsSymbolV2::Mixed;
1789  }
1790  return unit;
1791 }
1792 
1794 {
1796  mPatternWidthMapUnitScale = scale;
1798  mOutlineWidthMapUnitScale = scale;
1799 }
1800 
1802 {
1806  {
1808  }
1809  return QgsMapUnitScale();
1810 }
1811 
1813 {
1815  storeViewBox();
1816 
1817  mSvgFilePath = svgPath;
1818  setDefaultSvgParams();
1819 }
1820 
1822 {
1823  QByteArray data;
1824  double width = 20;
1826  double angle = 0.0;
1827 
1828  if ( properties.contains( "width" ) )
1829  {
1830  width = properties["width"].toDouble();
1831  }
1832  if ( properties.contains( "svgFile" ) )
1833  {
1834  QString svgName = properties["svgFile"];
1835  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
1836  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
1837  }
1838  if ( properties.contains( "angle" ) )
1839  {
1840  angle = properties["angle"].toDouble();
1841  }
1842 
1843  QgsSVGFillSymbolLayer* symbolLayer = nullptr;
1844  if ( !svgFilePath.isEmpty() )
1845  {
1846  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1847  }
1848  else
1849  {
1850  if ( properties.contains( "data" ) )
1851  {
1852  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1853  }
1854  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1855  }
1856 
1857  //svg parameters
1858  if ( properties.contains( "svgFillColor" ) )
1859  {
1860  //pre 2.5 projects used "svgFillColor"
1861  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgFillColor"] ) );
1862  }
1863  else if ( properties.contains( "color" ) )
1864  {
1865  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["color"] ) );
1866  }
1867  if ( properties.contains( "svgOutlineColor" ) )
1868  {
1869  //pre 2.5 projects used "svgOutlineColor"
1870  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgOutlineColor"] ) );
1871  }
1872  else if ( properties.contains( "outline_color" ) )
1873  {
1874  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
1875  }
1876  else if ( properties.contains( "line_color" ) )
1877  {
1878  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] ) );
1879  }
1880  if ( properties.contains( "svgOutlineWidth" ) )
1881  {
1882  //pre 2.5 projects used "svgOutlineWidth"
1883  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1884  }
1885  else if ( properties.contains( "outline_width" ) )
1886  {
1887  symbolLayer->setSvgOutlineWidth( properties["outline_width"].toDouble() );
1888  }
1889  else if ( properties.contains( "line_width" ) )
1890  {
1891  symbolLayer->setSvgOutlineWidth( properties["line_width"].toDouble() );
1892  }
1893 
1894  //units
1895  if ( properties.contains( "pattern_width_unit" ) )
1896  {
1897  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1898  }
1899  if ( properties.contains( "pattern_width_map_unit_scale" ) )
1900  {
1901  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["pattern_width_map_unit_scale"] ) );
1902  }
1903  if ( properties.contains( "svg_outline_width_unit" ) )
1904  {
1905  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1906  }
1907  if ( properties.contains( "svg_outline_width_map_unit_scale" ) )
1908  {
1909  symbolLayer->setSvgOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["svg_outline_width_map_unit_scale"] ) );
1910  }
1911  if ( properties.contains( "outline_width_unit" ) )
1912  {
1913  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1914  }
1915  if ( properties.contains( "outline_width_map_unit_scale" ) )
1916  {
1917  symbolLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
1918  }
1919 
1920  symbolLayer->restoreDataDefinedProperties( properties );
1921 
1922  return symbolLayer;
1923 }
1924 
1926 {
1927  return "SVGFill";
1928 }
1929 
1930 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1931  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1934 {
1935  if ( mSvgViewBox.isNull() )
1936  {
1937  return;
1938  }
1939 
1940  delete mSvgPattern;
1941  mSvgPattern = nullptr;
1943 
1944  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1945  {
1946  mSvgPattern = new QImage();
1947  brush.setTextureImage( *mSvgPattern );
1948  }
1949  else
1950  {
1951  bool fitsInCache = true;
1953  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1954  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1955  if ( !fitsInCache )
1956  {
1957  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1958  context.renderContext().scaleFactor(), 1.0 );
1959  double hwRatio = 1.0;
1960  if ( patternPict.width() > 0 )
1961  {
1962  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1963  }
1964  mSvgPattern = new QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1965  mSvgPattern->fill( 0 ); // transparent background
1966 
1967  QPainter p( mSvgPattern );
1968  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1969  }
1970 
1971  QTransform brushTransform;
1972  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1973  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1974  {
1975  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1976  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1977  brush.setTextureImage( transparentImage );
1978  }
1979  else
1980  {
1981  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1982  }
1983  brush.setTransform( brushTransform );
1984  }
1985 }
1986 
1988 {
1989 
1991 
1992  if ( mOutline )
1993  {
1994  mOutline->startRender( context.renderContext(), context.fields() );
1995  }
1996 
1997  prepareExpressions( context );
1998 }
1999 
2001 {
2002  if ( mOutline )
2003  {
2004  mOutline->stopRender( context.renderContext() );
2005  }
2006 }
2007 
2009 {
2010  QgsStringMap map;
2011  if ( !mSvgFilePath.isEmpty() )
2012  {
2014  }
2015  else
2016  {
2017  map.insert( "data", QString( mSvgData.toHex() ) );
2018  }
2019 
2020  map.insert( "width", QString::number( mPatternWidth ) );
2021  map.insert( "angle", QString::number( mAngle ) );
2022 
2023  //svg parameters
2025  map.insert( "outline_color", QgsSymbolLayerV2Utils::encodeColor( mSvgOutlineColor ) );
2026  map.insert( "outline_width", QString::number( mSvgOutlineWidth ) );
2027 
2028  //units
2029  map.insert( "pattern_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit ) );
2030  map.insert( "pattern_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2031  map.insert( "svg_outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit ) );
2032  map.insert( "svg_outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mSvgOutlineWidthMapUnitScale ) );
2033  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2034  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2035 
2037  return map;
2038 }
2039 
2041 {
2042  QgsSVGFillSymbolLayer* clonedLayer = nullptr;
2043  if ( !mSvgFilePath.isEmpty() )
2044  {
2045  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
2046  clonedLayer->setSvgFillColor( mColor );
2047  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
2048  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
2049  }
2050  else
2051  {
2052  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
2053  }
2054 
2055  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2059  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
2061 
2062  if ( mOutline )
2063  {
2064  clonedLayer->setSubSymbol( mOutline->clone() );
2065  }
2066  copyDataDefinedProperties( clonedLayer );
2067  copyPaintEffect( clonedLayer );
2068  return clonedLayer;
2069 }
2070 
2071 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
2072 {
2073  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2074  if ( !props.value( "uom", "" ).isEmpty() )
2075  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2076  element.appendChild( symbolizerElem );
2077 
2078  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2079 
2080  QDomElement fillElem = doc.createElement( "se:Fill" );
2081  symbolizerElem.appendChild( fillElem );
2082 
2083  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2084  fillElem.appendChild( graphicFillElem );
2085 
2086  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2087  graphicFillElem.appendChild( graphicElem );
2088 
2089  if ( !mSvgFilePath.isEmpty() )
2090  {
2091  QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, mPatternWidth );
2092  }
2093  else
2094  {
2095  // TODO: create svg from data
2096  // <se:InlineContent>
2097  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
2098  }
2099 
2100  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
2101  {
2102  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
2103  }
2104 
2105  // <Rotation>
2106  QString angleFunc;
2107  bool ok;
2108  double angle = props.value( "angle", "0" ).toDouble( &ok );
2109  if ( !ok )
2110  {
2111  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
2112  }
2113  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2114  {
2115  angleFunc = QString::number( angle + mAngle );
2116  }
2117  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2118 
2119  if ( mOutline )
2120  {
2121  // the outline sub symbol should be stored within the Stroke element,
2122  // but it will be stored in a separated LineSymbolizer because it could
2123  // have more than one layer
2124  mOutline->toSld( doc, element, props );
2125  }
2126 }
2127 
2129 {
2130  QgsDebugMsg( "Entered." );
2131 
2132  QString path, mimeType;
2133  QColor fillColor, borderColor;
2134  Qt::PenStyle penStyle;
2135  double size, borderWidth;
2136 
2137  QDomElement fillElem = element.firstChildElement( "Fill" );
2138  if ( fillElem.isNull() )
2139  return nullptr;
2140 
2141  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2142  if ( graphicFillElem.isNull() )
2143  return nullptr;
2144 
2145  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2146  if ( graphicElem.isNull() )
2147  return nullptr;
2148 
2149  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2150  return nullptr;
2151 
2152  if ( mimeType != "image/svg+xml" )
2153  return nullptr;
2154 
2155  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
2156 
2157  double angle = 0.0;
2158  QString angleFunc;
2159  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2160  {
2161  bool ok;
2162  double d = angleFunc.toDouble( &ok );
2163  if ( ok )
2164  angle = d;
2165  }
2166 
2167  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
2168  sl->setSvgFillColor( fillColor );
2169  sl->setSvgOutlineColor( borderColor );
2170  sl->setSvgOutlineWidth( borderWidth );
2171 
2172  // try to get the outline
2173  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2174  if ( !strokeElem.isNull() )
2175  {
2177  if ( l )
2178  {
2179  QgsSymbolLayerV2List layers;
2180  layers.append( l );
2181  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2182  }
2183  }
2184 
2185  return sl;
2186 }
2187 
2189 {
2193  {
2194  return; //no data defined settings
2195  }
2196 
2197  bool ok;
2198 
2200  {
2201  context.setOriginalValueVariable( mAngle );
2202  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
2203  if ( ok )
2204  mNextAngle = nextAngle;
2205  }
2206 
2207  double width = mPatternWidth;
2209  {
2212  }
2213  QString svgFile = mSvgFilePath;
2215  {
2218  }
2219  QColor svgFillColor = mColor;
2221  {
2224  if ( ok )
2225  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2226  }
2227  QColor svgOutlineColor = mSvgOutlineColor;
2229  {
2232  if ( ok )
2233  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2234  }
2235  double outlineWidth = mSvgOutlineWidth;
2237  {
2240  }
2241  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
2243 
2244 }
2245 
2246 void QgsSVGFillSymbolLayer::storeViewBox()
2247 {
2248  if ( !mSvgData.isEmpty() )
2249  {
2250  QSvgRenderer r( mSvgData );
2251  if ( r.isValid() )
2252  {
2253  mSvgViewBox = r.viewBoxF();
2254  return;
2255  }
2256  }
2257 
2258  mSvgViewBox = QRectF();
2259  return;
2260 }
2261 
2262 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2263 {
2264  if ( mSvgFilePath.isEmpty() )
2265  {
2266  return;
2267  }
2268 
2269  bool hasFillParam, hasFillOpacityParam, hasOutlineParam, hasOutlineWidthParam, hasOutlineOpacityParam;
2270  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultOutlineColor, hasDefaultOutlineWidth, hasDefaultOutlineOpacity;
2271  QColor defaultFillColor, defaultOutlineColor;
2272  double defaultOutlineWidth, defaultFillOpacity, defaultOutlineOpacity;
2273  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2274  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2275  hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
2276  hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
2277  hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
2278 
2279  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2280  double newOutlineOpacity = hasOutlineOpacityParam ? mSvgOutlineColor.alphaF() : 1.0;
2281 
2282  if ( hasDefaultFillColor )
2283  {
2284  mColor = defaultFillColor;
2285  mColor.setAlphaF( newFillOpacity );
2286  }
2287  if ( hasDefaultFillOpacity )
2288  {
2289  mColor.setAlphaF( defaultFillOpacity );
2290  }
2291  if ( hasDefaultOutlineColor )
2292  {
2293  mSvgOutlineColor = defaultOutlineColor;
2294  mSvgOutlineColor.setAlphaF( newOutlineOpacity );
2295  }
2296  if ( hasDefaultOutlineOpacity )
2297  {
2298  mSvgOutlineColor.setAlphaF( defaultOutlineOpacity );
2299  }
2300  if ( hasDefaultOutlineWidth )
2301  {
2302  mSvgOutlineWidth = defaultOutlineWidth;
2303  }
2304 }
2305 
2306 
2309  , mDistance( 5.0 )
2310  , mDistanceUnit( QgsSymbolV2::MM )
2311  , mLineWidth( 0 )
2312  , mLineWidthUnit( QgsSymbolV2::MM )
2313  , mLineAngle( 45.0 )
2314  , mOffset( 0.0 )
2315  , mOffsetUnit( QgsSymbolV2::MM )
2316  , mFillLineSymbol( nullptr )
2317 {
2318  setSubSymbol( new QgsLineSymbolV2() );
2319  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
2320 }
2321 
2323 {
2324  mFillLineSymbol->setWidth( w );
2325  mLineWidth = w;
2326 }
2327 
2329 {
2330  mFillLineSymbol->setColor( c );
2331  mColor = c;
2332 }
2333 
2335 {
2336  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2337 }
2338 
2340 {
2341  delete mFillLineSymbol;
2342 }
2343 
2345 {
2346  if ( !symbol )
2347  {
2348  return false;
2349  }
2350 
2351  if ( symbol->type() == QgsSymbolV2::Line )
2352  {
2353  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
2354  if ( lineSymbol )
2355  {
2356  delete mFillLineSymbol;
2357  mFillLineSymbol = lineSymbol;
2358 
2359  return true;
2360  }
2361  }
2362  delete symbol;
2363  return false;
2364 }
2365 
2367 {
2368  return mFillLineSymbol;
2369 }
2370 
2372 {
2374  if ( mFillLineSymbol )
2375  attr.unite( mFillLineSymbol->usedAttributes() );
2376  return attr;
2377 }
2378 
2380 {
2381  return 0;
2382 }
2383 
2385 {
2387  mDistanceUnit = unit;
2388  mLineWidthUnit = unit;
2389  mOffsetUnit = unit;
2390 }
2391 
2393 {
2395  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2396  {
2397  return QgsSymbolV2::Mixed;
2398  }
2399  return unit;
2400 }
2401 
2403 {
2405  mDistanceMapUnitScale = scale;
2406  mLineWidthMapUnitScale = scale;
2407  mOffsetMapUnitScale = scale;
2408 }
2409 
2411 {
2415  {
2416  return mDistanceMapUnitScale;
2417  }
2418  return QgsMapUnitScale();
2419 }
2420 
2422 {
2424 
2425  //default values
2426  double lineAngle = 45;
2427  double distance = 5;
2428  double lineWidth = 0.5;
2429  QColor color( Qt::black );
2430  double offset = 0.0;
2431 
2432  if ( properties.contains( "lineangle" ) )
2433  {
2434  //pre 2.5 projects used "lineangle"
2435  lineAngle = properties["lineangle"].toDouble();
2436  }
2437  else if ( properties.contains( "angle" ) )
2438  {
2439  lineAngle = properties["angle"].toDouble();
2440  }
2441  patternLayer->setLineAngle( lineAngle );
2442 
2443  if ( properties.contains( "distance" ) )
2444  {
2445  distance = properties["distance"].toDouble();
2446  }
2447  patternLayer->setDistance( distance );
2448 
2449  if ( properties.contains( "linewidth" ) )
2450  {
2451  //pre 2.5 projects used "linewidth"
2452  lineWidth = properties["linewidth"].toDouble();
2453  }
2454  else if ( properties.contains( "outline_width" ) )
2455  {
2456  lineWidth = properties["outline_width"].toDouble();
2457  }
2458  else if ( properties.contains( "line_width" ) )
2459  {
2460  lineWidth = properties["line_width"].toDouble();
2461  }
2462  patternLayer->setLineWidth( lineWidth );
2463 
2464  if ( properties.contains( "color" ) )
2465  {
2466  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
2467  }
2468  else if ( properties.contains( "outline_color" ) )
2469  {
2470  color = QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] );
2471  }
2472  else if ( properties.contains( "line_color" ) )
2473  {
2474  color = QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] );
2475  }
2476  patternLayer->setColor( color );
2477 
2478  if ( properties.contains( "offset" ) )
2479  {
2480  offset = properties["offset"].toDouble();
2481  }
2482  patternLayer->setOffset( offset );
2483 
2484 
2485  if ( properties.contains( "distance_unit" ) )
2486  {
2487  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
2488  }
2489  if ( properties.contains( "distance_map_unit_scale" ) )
2490  {
2491  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_map_unit_scale"] ) );
2492  }
2493  if ( properties.contains( "line_width_unit" ) )
2494  {
2495  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
2496  }
2497  else if ( properties.contains( "outline_width_unit" ) )
2498  {
2499  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2500  }
2501  if ( properties.contains( "line_width_map_unit_scale" ) )
2502  {
2503  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["line_width_map_unit_scale"] ) );
2504  }
2505  if ( properties.contains( "offset_unit" ) )
2506  {
2507  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
2508  }
2509  if ( properties.contains( "offset_map_unit_scale" ) )
2510  {
2511  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
2512  }
2513  if ( properties.contains( "outline_width_unit" ) )
2514  {
2515  patternLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2516  }
2517  if ( properties.contains( "outline_width_map_unit_scale" ) )
2518  {
2519  patternLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
2520  }
2521 
2522  patternLayer->restoreDataDefinedProperties( properties );
2523 
2524  return patternLayer;
2525 }
2526 
2528 {
2529  return "LinePatternFill";
2530 }
2531 
2532 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
2533  double lineWidth, const QColor& color )
2534 {
2535  Q_UNUSED( lineWidth );
2536  Q_UNUSED( color );
2537 
2538  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2539 
2540  if ( !mFillLineSymbol )
2541  {
2542  return;
2543  }
2544  // We have to make a copy because marker intervals will have to be adjusted
2545  QgsLineSymbolV2* fillLineSymbol = mFillLineSymbol->clone();
2546  if ( !fillLineSymbol )
2547  {
2548  return;
2549  }
2550 
2551  const QgsRenderContext& ctx = context.renderContext();
2552  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2555 
2556  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2557  // For marker lines we have to get markers interval.
2558  double outputPixelBleed = 0;
2559  double outputPixelInterval = 0; // maximum interval
2560  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2561  {
2562  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2563  double layerBleed = layer->estimateMaxBleed();
2564  // TODO: to get real bleed we have to scale it using context and units,
2565  // unfortunately estimateMaxBleed() ignore units completely, e.g.
2566  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
2567  // offset regardless units. This has to be fixed especially
2568  // in estimateMaxBleed(), context probably has to be used.
2569  // For now, we only support millimeters
2570  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
2571  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
2572 
2573  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2574  if ( markerLineLayer )
2575  {
2576  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2577 
2578  // There may be multiple marker lines with different intervals.
2579  // In theory we should find the least common multiple, but that could be too
2580  // big (multiplication of intervals in the worst case).
2581  // Because patterns without small common interval would look strange, we
2582  // believe that the longest interval should usually be sufficient.
2583  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
2584  }
2585  }
2586 
2587  if ( outputPixelInterval > 0 )
2588  {
2589  // We have to adjust marker intervals to integer pixel size to get
2590  // repeatable pattern.
2591  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
2592  outputPixelInterval = qRound( outputPixelInterval );
2593 
2594  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2595  {
2596  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2597 
2598  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2599  if ( markerLineLayer )
2600  {
2601  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2602  }
2603  }
2604  }
2605 
2606  //create image
2607  int height, width;
2608  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2609  {
2610  height = outputPixelDist;
2611  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2612  }
2613  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2614  {
2615  width = outputPixelDist;
2616  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2617  }
2618  else
2619  {
2620  height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2621  width = outputPixelDist / sin( lineAngle * M_PI / 180 );
2622 
2623  // recalculate real angle and distance after rounding to pixels
2624  lineAngle = 180 * atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2625  if ( lineAngle < 0 )
2626  {
2627  lineAngle += 360.;
2628  }
2629 
2630  height = qAbs( height );
2631  width = qAbs( width );
2632 
2633  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
2634 
2635  // Round offset to correspond to one pixel height, otherwise lines may
2636  // be shifted on tile border if offset falls close to pixel center
2637  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
2638  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
2639  }
2640 
2641  //depending on the angle, we might need to render into a larger image and use a subset of it
2642  double dx = 0;
2643  double dy = 0;
2644 
2645  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2646  // thus we add integer multiplications of width and height covering the bleed
2647  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
2648 
2649  // Always buffer at least once so that center of line marker in upper right corner
2650  // does not fall outside due to representation error
2651  bufferMulti = qMax( bufferMulti, 1 );
2652 
2653  int xBuffer = width * bufferMulti;
2654  int yBuffer = height * bufferMulti;
2655  int innerWidth = width;
2656  int innerHeight = height;
2657  width += 2 * xBuffer;
2658  height += 2 * yBuffer;
2659 
2660  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2661  {
2662  return;
2663  }
2664 
2665  QImage patternImage( width, height, QImage::Format_ARGB32 );
2666  patternImage.fill( 0 );
2667 
2668  QPointF p1, p2, p3, p4, p5, p6;
2669  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2670  {
2671  p1 = QPointF( 0, yBuffer );
2672  p2 = QPointF( width, yBuffer );
2673  p3 = QPointF( 0, yBuffer + innerHeight );
2674  p4 = QPointF( width, yBuffer + innerHeight );
2675  }
2676  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2677  {
2678  p1 = QPointF( xBuffer, height );
2679  p2 = QPointF( xBuffer, 0 );
2680  p3 = QPointF( xBuffer + innerWidth, height );
2681  p4 = QPointF( xBuffer + innerWidth, 0 );
2682  }
2683  else if ( lineAngle > 0 && lineAngle < 90 )
2684  {
2685  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2686  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2687  p1 = QPointF( 0, height );
2688  p2 = QPointF( width, 0 );
2689  p3 = QPointF( -dx, height - dy );
2690  p4 = QPointF( width - dx, -dy );
2691  p5 = QPointF( dx, height + dy );
2692  p6 = QPointF( width + dx, dy );
2693  }
2694  else if ( lineAngle > 180 && lineAngle < 270 )
2695  {
2696  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2697  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2698  p1 = QPointF( width, 0 );
2699  p2 = QPointF( 0, height );
2700  p3 = QPointF( width - dx, -dy );
2701  p4 = QPointF( -dx, height - dy );
2702  p5 = QPointF( width + dx, dy );
2703  p6 = QPointF( dx, height + dy );
2704  }
2705  else if ( lineAngle > 90 && lineAngle < 180 )
2706  {
2707  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2708  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2709  p1 = QPointF( 0, 0 );
2710  p2 = QPointF( width, height );
2711  p5 = QPointF( dx, -dy );
2712  p6 = QPointF( width + dx, height - dy );
2713  p3 = QPointF( -dx, dy );
2714  p4 = QPointF( width - dx, height + dy );
2715  }
2716  else if ( lineAngle > 270 && lineAngle < 360 )
2717  {
2718  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2719  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2720  p1 = QPointF( width, height );
2721  p2 = QPointF( 0, 0 );
2722  p5 = QPointF( width + dx, height - dy );
2723  p6 = QPointF( dx, -dy );
2724  p3 = QPointF( width - dx, height + dy );
2725  p4 = QPointF( -dx, dy );
2726  }
2727 
2728  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2729  {
2730  QPointF tempPt;
2731  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2732  p3 = QPointF( tempPt.x(), tempPt.y() );
2733  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2734  p4 = QPointF( tempPt.x(), tempPt.y() );
2735  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2736  p5 = QPointF( tempPt.x(), tempPt.y() );
2737  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2738  p6 = QPointF( tempPt.x(), tempPt.y() );
2739 
2740  //update p1, p2 last
2741  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2742  p1 = QPointF( tempPt.x(), tempPt.y() );
2743  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2744  p2 = QPointF( tempPt.x(), tempPt.y() );
2745  }
2746 
2747  QPainter p( &patternImage );
2748 
2749 #if 0
2750  // DEBUG: Draw rectangle
2751  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2752  QPen pen( QColor( Qt::black ) );
2753  pen.setWidthF( 0.1 );
2754  pen.setCapStyle( Qt::FlatCap );
2755  p.setPen( pen );
2756 
2757  // To see this rectangle, comment buffer cut below.
2758  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2759  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2760  p.drawPolygon( polygon );
2761 
2762  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 );
2763  p.drawPolygon( polygon );
2764 #endif
2765 
2766  // Use antialiasing because without antialiasing lines are rendered to the
2767  // right and below the mathematically defined points (not symmetrical)
2768  // and such tiles become useless for are filling
2769  p.setRenderHint( QPainter::Antialiasing, true );
2770 
2771  // line rendering needs context for drawing on patternImage
2772  QgsRenderContext lineRenderContext;
2773  lineRenderContext.setPainter( &p );
2774  lineRenderContext.setRasterScaleFactor( 1.0 );
2775  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2777  lineRenderContext.setMapToPixel( mtp );
2778  lineRenderContext.setForceVectorOutput( false );
2779  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2780 
2781  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2782 
2783  QVector<QPolygonF> polygons;
2784  polygons.append( QPolygonF() << p1 << p2 );
2785  polygons.append( QPolygonF() << p3 << p4 );
2786  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2787  {
2788  polygons.append( QPolygonF() << p5 << p6 );
2789  }
2790 
2791  Q_FOREACH ( const QPolygonF& polygon, polygons )
2792  {
2793  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2794  }
2795 
2796  fillLineSymbol->stopRender( lineRenderContext );
2797  p.end();
2798 
2799  // Cut off the buffer
2800  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2801 
2802  //set image to mBrush
2803  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2804  {
2805  QImage transparentImage = patternImage.copy();
2806  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2807  brush.setTextureImage( transparentImage );
2808  }
2809  else
2810  {
2811  brush.setTextureImage( patternImage );
2812  }
2813 
2814  QTransform brushTransform;
2815  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2816  brush.setTransform( brushTransform );
2817 
2818  delete fillLineSymbol;
2819 }
2820 
2822 {
2823  applyPattern( context, mBrush, mLineAngle, mDistance, mLineWidth, mColor );
2824 
2825  if ( mFillLineSymbol )
2826  {
2827  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2828  }
2829 
2830  prepareExpressions( context );
2831 }
2832 
2834 {
2835 }
2836 
2838 {
2839  QgsStringMap map;
2840  map.insert( "angle", QString::number( mLineAngle ) );
2841  map.insert( "distance", QString::number( mDistance ) );
2842  map.insert( "line_width", QString::number( mLineWidth ) );
2844  map.insert( "offset", QString::number( mOffset ) );
2846  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
2848  map.insert( "distance_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2849  map.insert( "line_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2850  map.insert( "offset_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2851  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2852  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2854  return map;
2855 }
2856 
2858 {
2860  if ( mFillLineSymbol )
2861  {
2862  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2863  }
2864  copyPaintEffect( clonedLayer );
2865  return clonedLayer;
2866 }
2867 
2869 {
2870  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2871  if ( !props.value( "uom", "" ).isEmpty() )
2872  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2873  element.appendChild( symbolizerElem );
2874 
2875  // <Geometry>
2876  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2877 
2878  QDomElement fillElem = doc.createElement( "se:Fill" );
2879  symbolizerElem.appendChild( fillElem );
2880 
2881  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2882  fillElem.appendChild( graphicFillElem );
2883 
2884  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2885  graphicFillElem.appendChild( graphicElem );
2886 
2887  //line properties must be inside the graphic definition
2888  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2889  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2890  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
2891 
2892  // <Rotation>
2893  QString angleFunc;
2894  bool ok;
2895  double angle = props.value( "angle", "0" ).toDouble( &ok );
2896  if ( !ok )
2897  {
2898  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
2899  }
2900  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2901  {
2902  angleFunc = QString::number( angle + mLineAngle );
2903  }
2904  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2905 
2906  // <se:Displacement>
2907  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2908  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
2909 }
2910 
2912 {
2913  QString featureStyle;
2914  featureStyle.append( "Brush(" );
2915  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
2916  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
2917  featureStyle.append( ",id:\"ogr-brush-2\"" );
2918  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
2919  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2920  featureStyle.append( ",dx:0mm" );
2921  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2922  featureStyle.append( ')' );
2923  return featureStyle;
2924 }
2925 
2927 {
2930  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2931  {
2932  return; //no data defined settings
2933  }
2934 
2935  bool ok;
2936  double lineAngle = mLineAngle;
2938  {
2941  }
2942  double distance = mDistance;
2944  {
2947  }
2948  double lineWidth = mLineWidth;
2950  {
2953  }
2954  QColor color = mColor;
2956  {
2959  if ( ok )
2960  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
2961  }
2962  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
2963 }
2964 
2966 {
2967  QgsDebugMsg( "Entered." );
2968 
2969  QString name;
2970  QColor fillColor, lineColor;
2971  double size, lineWidth;
2972  Qt::PenStyle lineStyle;
2973 
2974  QDomElement fillElem = element.firstChildElement( "Fill" );
2975  if ( fillElem.isNull() )
2976  return nullptr;
2977 
2978  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2979  if ( graphicFillElem.isNull() )
2980  return nullptr;
2981 
2982  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2983  if ( graphicElem.isNull() )
2984  return nullptr;
2985 
2986  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2987  return nullptr;
2988 
2989  if ( name != "horline" )
2990  return nullptr;
2991 
2992  double angle = 0.0;
2993  QString angleFunc;
2994  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2995  {
2996  bool ok;
2997  double d = angleFunc.toDouble( &ok );
2998  if ( ok )
2999  angle = d;
3000  }
3001 
3002  double offset = 0.0;
3003  QPointF vectOffset;
3004  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
3005  {
3006  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
3007  }
3008 
3010  sl->setColor( lineColor );
3011  sl->setLineWidth( lineWidth );
3012  sl->setLineAngle( angle );
3013  sl->setOffset( offset );
3014  sl->setDistance( size );
3015 
3016  // try to get the outline
3017  QDomElement strokeElem = element.firstChildElement( "Stroke" );
3018  if ( !strokeElem.isNull() )
3019  {
3021  if ( l )
3022  {
3023  QgsSymbolLayerV2List layers;
3024  layers.append( l );
3025  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
3026  }
3027  }
3028 
3029  return sl;
3030 }
3031 
3032 
3034 
3037  , mMarkerSymbol( nullptr )
3038  , mDistanceX( 15 )
3039  , mDistanceXUnit( QgsSymbolV2::MM )
3040  , mDistanceY( 15 )
3041  , mDistanceYUnit( QgsSymbolV2::MM )
3042  , mDisplacementX( 0 )
3043  , mDisplacementXUnit( QgsSymbolV2::MM )
3044  , mDisplacementY( 0 )
3045  , mDisplacementYUnit( QgsSymbolV2::MM )
3046 {
3047  mDistanceX = 15;
3048  mDistanceY = 15;
3049  mDisplacementX = 0;
3050  mDisplacementY = 0;
3052  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
3053 }
3054 
3056 {
3057  delete mMarkerSymbol;
3058 }
3059 
3061 {
3063  mDistanceXUnit = unit;
3064  mDistanceYUnit = unit;
3065  mDisplacementXUnit = unit;
3066  mDisplacementYUnit = unit;
3067 }
3068 
3070 {
3072  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3073  {
3074  return QgsSymbolV2::Mixed;
3075  }
3076  return unit;
3077 }
3078 
3080 {
3082  mDistanceXMapUnitScale = scale;
3083  mDistanceYMapUnitScale = scale;
3086 }
3087 
3089 {
3094  {
3095  return mDistanceXMapUnitScale;
3096  }
3097  return QgsMapUnitScale();
3098 }
3099 
3101 {
3103  if ( properties.contains( "distance_x" ) )
3104  {
3105  layer->setDistanceX( properties["distance_x"].toDouble() );
3106  }
3107  if ( properties.contains( "distance_y" ) )
3108  {
3109  layer->setDistanceY( properties["distance_y"].toDouble() );
3110  }
3111  if ( properties.contains( "displacement_x" ) )
3112  {
3113  layer->setDisplacementX( properties["displacement_x"].toDouble() );
3114  }
3115  if ( properties.contains( "displacement_y" ) )
3116  {
3117  layer->setDisplacementY( properties["displacement_y"].toDouble() );
3118  }
3119 
3120  if ( properties.contains( "distance_x_unit" ) )
3121  {
3122  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
3123  }
3124  if ( properties.contains( "distance_x_map_unit_scale" ) )
3125  {
3126  layer->setDistanceXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_x_map_unit_scale"] ) );
3127  }
3128  if ( properties.contains( "distance_y_unit" ) )
3129  {
3130  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
3131  }
3132  if ( properties.contains( "distance_y_map_unit_scale" ) )
3133  {
3134  layer->setDistanceYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_y_map_unit_scale"] ) );
3135  }
3136  if ( properties.contains( "displacement_x_unit" ) )
3137  {
3138  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
3139  }
3140  if ( properties.contains( "displacement_x_map_unit_scale" ) )
3141  {
3142  layer->setDisplacementXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_x_map_unit_scale"] ) );
3143  }
3144  if ( properties.contains( "displacement_y_unit" ) )
3145  {
3146  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
3147  }
3148  if ( properties.contains( "displacement_y_map_unit_scale" ) )
3149  {
3150  layer->setDisplacementYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_y_map_unit_scale"] ) );
3151  }
3152  if ( properties.contains( "outline_width_unit" ) )
3153  {
3154  layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
3155  }
3156  if ( properties.contains( "outline_width_map_unit_scale" ) )
3157  {
3158  layer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
3159  }
3160 
3161  layer->restoreDataDefinedProperties( properties );
3162 
3163  return layer;
3164 }
3165 
3167 {
3168  return "PointPatternFill";
3169 }
3170 
3171 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
3172  double displacementX, double displacementY )
3173 {
3174  //render 3 rows and columns in one go to easily incorporate displacement
3175  const QgsRenderContext& ctx = context.renderContext();
3178 
3179  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3180  {
3181  QImage img;
3182  brush.setTextureImage( img );
3183  return;
3184  }
3185 
3186  QImage patternImage( width, height, QImage::Format_ARGB32 );
3187  patternImage.fill( 0 );
3188 
3189  if ( mMarkerSymbol )
3190  {
3191  QPainter p( &patternImage );
3192 
3193  //marker rendering needs context for drawing on patternImage
3194  QgsRenderContext pointRenderContext;
3195  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3196  pointRenderContext.setPainter( &p );
3197  pointRenderContext.setRasterScaleFactor( 1.0 );
3198  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
3200  pointRenderContext.setMapToPixel( mtp );
3201  pointRenderContext.setForceVectorOutput( false );
3202  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3203 
3204  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3205 
3206  //render corner points
3207  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3208  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3209  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3210  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3211 
3212  //render displaced points
3214  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit, mDisplacementYMapUnitScale );
3215  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3216  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3217  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3218  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3219  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3220 
3221  mMarkerSymbol->stopRender( pointRenderContext );
3222  }
3223 
3224  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
3225  {
3226  QImage transparentImage = patternImage.copy();
3227  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
3228  brush.setTextureImage( transparentImage );
3229  }
3230  else
3231  {
3232  brush.setTextureImage( patternImage );
3233  }
3234  QTransform brushTransform;
3235  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
3236  brush.setTransform( brushTransform );
3237 }
3238 
3240 {
3241  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3242 
3243  if ( mOutline )
3244  {
3245  mOutline->startRender( context.renderContext(), context.fields() );
3246  }
3247  prepareExpressions( context );
3248 }
3249 
3251 {
3252  if ( mOutline )
3253  {
3254  mOutline->stopRender( context.renderContext() );
3255  }
3256 }
3257 
3259 {
3260  QgsStringMap map;
3261  map.insert( "distance_x", QString::number( mDistanceX ) );
3262  map.insert( "distance_y", QString::number( mDistanceY ) );
3263  map.insert( "displacement_x", QString::number( mDisplacementX ) );
3264  map.insert( "displacement_y", QString::number( mDisplacementY ) );
3265  map.insert( "distance_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit ) );
3266  map.insert( "distance_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit ) );
3267  map.insert( "displacement_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit ) );
3268  map.insert( "displacement_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit ) );
3269  map.insert( "distance_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3270  map.insert( "distance_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3271  map.insert( "displacement_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3272  map.insert( "displacement_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3273  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
3274  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
3276  return map;
3277 }
3278 
3280 {
3282  if ( mMarkerSymbol )
3283  {
3284  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3285  }
3286  copyPaintEffect( clonedLayer );
3287  return clonedLayer;
3288 }
3289 
3291 {
3292  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3293  {
3294  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
3295  if ( !props.value( "uom", "" ).isEmpty() )
3296  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
3297  element.appendChild( symbolizerElem );
3298 
3299  // <Geometry>
3300  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
3301 
3302  QDomElement fillElem = doc.createElement( "se:Fill" );
3303  symbolizerElem.appendChild( fillElem );
3304 
3305  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
3306  fillElem.appendChild( graphicFillElem );
3307 
3308  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3310  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
3311  symbolizerElem.appendChild( distanceElem );
3312 
3314  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
3315  if ( !markerLayer )
3316  {
3317  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3318  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3319  }
3320  else
3321  {
3322  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3323  }
3324  }
3325 }
3326 
3328 {
3329  Q_UNUSED( element );
3330  return nullptr;
3331 }
3332 
3334 {
3335  if ( !symbol )
3336  {
3337  return false;
3338  }
3339 
3340  if ( symbol->type() == QgsSymbolV2::Marker )
3341  {
3342  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
3343  delete mMarkerSymbol;
3344  mMarkerSymbol = markerSymbol;
3345  }
3346  return true;
3347 }
3348 
3350 {
3354  {
3355  return;
3356  }
3357 
3358  double distanceX = mDistanceX;
3360  {
3363  }
3364  double distanceY = mDistanceY;
3366  {
3369  }
3370  double displacementX = mDisplacementX;
3372  {
3375  }
3376  double displacementY = mDisplacementY;
3378  {
3381  }
3382  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3383 }
3384 
3386 {
3387  return 0;
3388 }
3389 
3391 {
3393 
3394  if ( mMarkerSymbol )
3395  attributes.unite( mMarkerSymbol->usedAttributes() );
3396 
3397  return attributes;
3398 }
3399 
3401 {
3402  mColor = c;
3403  if ( mMarkerSymbol )
3404  mMarkerSymbol->setColor( c );
3405 }
3406 
3408 {
3409  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3410 }
3411 
3413 
3414 
3416  : mMarker( nullptr )
3417  , mPointOnSurface( false )
3418  , mPointOnAllParts( true )
3419  , mCurrentFeatureId( -1 )
3420  , mBiggestPartIndex( -1 )
3421 {
3423 }
3424 
3426 {
3427  delete mMarker;
3428 }
3429 
3431 {
3433 
3434  if ( properties.contains( "point_on_surface" ) )
3435  sl->setPointOnSurface( properties["point_on_surface"].toInt() != 0 );
3436  if ( properties.contains( "point_on_all_parts" ) )
3437  sl->setPointOnAllParts( properties["point_on_all_parts"].toInt() != 0 );
3438 
3439  return sl;
3440 }
3441 
3443 {
3444  return "CentroidFill";
3445 }
3446 
3448 {
3449  mMarker->setColor( color );
3450  mColor = color;
3451 }
3452 
3454 {
3455  return mMarker ? mMarker->color() : mColor;
3456 }
3457 
3459 {
3460  mMarker->setAlpha( context.alpha() );
3461  mMarker->startRender( context.renderContext(), context.fields() );
3462 
3463  mCurrentFeatureId = -1;
3464  mBiggestPartIndex = 0;
3465 }
3466 
3468 {
3469  mMarker->stopRender( context.renderContext() );
3470 }
3471 
3473 {
3474  Q_UNUSED( rings );
3475 
3476  if ( !mPointOnAllParts )
3477  {
3478  const QgsFeature* feature = context.feature();
3479  if ( feature )
3480  {
3481  if ( feature->id() != mCurrentFeatureId )
3482  {
3483  mCurrentFeatureId = feature->id();
3484  mBiggestPartIndex = 1;
3485 
3486  if ( context.geometryPartCount() > 1 )
3487  {
3488  const QgsGeometry *geom = feature->constGeometry();
3489  const QgsGeometryCollectionV2* geomCollection = static_cast<const QgsGeometryCollectionV2*>( geom->geometry() );
3490 
3491  double area = 0;
3492  double areaBiggest = 0;
3493  for ( int i = 0; i < context.geometryPartCount(); ++i )
3494  {
3495  area = geomCollection->geometryN( i )->area();
3496  if ( area > areaBiggest )
3497  {
3498  areaBiggest = area;
3499  mBiggestPartIndex = i + 1;
3500  }
3501  }
3502  }
3503  }
3504  }
3505  }
3506 
3507  QgsDebugMsg( QString( "num: %1, count: %2" ).arg( context.geometryPartNum() ).arg( context.geometryPartCount() ) );
3508 
3509  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3510  {
3512  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3513  }
3514 }
3515 
3517 {
3518  QgsStringMap map;
3519  map["point_on_surface"] = QString::number( mPointOnSurface );
3520  map["point_on_all_parts"] = QString::number( mPointOnAllParts );
3521  return map;
3522 }
3523 
3525 {
3527  x->mAngle = mAngle;
3528  x->mColor = mColor;
3529  x->setSubSymbol( mMarker->clone() );
3532  copyPaintEffect( x );
3533  return x;
3534 }
3535 
3537 {
3538  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3539  // used with PointSymbolizer, then the semantic is to use the centroid
3540  // of the geometry, or any similar representative point.
3541  mMarker->toSld( doc, element, props );
3542 }
3543 
3545 {
3546  QgsDebugMsg( "Entered." );
3547 
3549  if ( !l )
3550  return nullptr;
3551 
3552  QgsSymbolLayerV2List layers;
3553  layers.append( l );
3554  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
3555 
3557  sl->setSubSymbol( marker );
3558  return sl;
3559 }
3560 
3561 
3563 {
3564  return mMarker;
3565 }
3566 
3568 {
3569  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
3570  {
3571  delete symbol;
3572  return false;
3573  }
3574 
3575  delete mMarker;
3576  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
3577  mColor = mMarker->color();
3578  return true;
3579 }
3580 
3582 {
3584 
3585  if ( mMarker )
3586  attributes.unite( mMarker->usedAttributes() );
3587 
3588  return attributes;
3589 }
3590 
3592 {
3593  if ( mMarker )
3594  {
3595  mMarker->setOutputUnit( unit );
3596  }
3597 }
3598 
3600 {
3601  if ( mMarker )
3602  {
3603  return mMarker->outputUnit();
3604  }
3605  return QgsSymbolV2::Mixed; //mOutputUnit;
3606 }
3607 
3609 {
3610  if ( mMarker )
3611  {
3612  mMarker->setMapUnitScale( scale );
3613  }
3614 }
3615 
3617 {
3618  if ( mMarker )
3619  {
3620  return mMarker->mapUnitScale();
3621  }
3622  return QgsMapUnitScale();
3623 }
3624 
3625 
3626 
3627 
3630  , mImageFilePath( imageFilePath )
3631  , mCoordinateMode( QgsRasterFillSymbolLayer::Feature )
3632  , mAlpha( 1.0 )
3633  , mOffsetUnit( QgsSymbolV2::MM )
3634  , mWidth( 0.0 )
3635  , mWidthUnit( QgsSymbolV2::Pixel )
3636 {
3637  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3638 }
3639 
3641 {
3642 
3643 }
3644 
3646 {
3648  double alpha = 1.0;
3649  QPointF offset;
3650  double angle = 0.0;
3651  double width = 0.0;
3652 
3653  QString imagePath;
3654  if ( properties.contains( "imageFile" ) )
3655  {
3656  imagePath = properties["imageFile"];
3657  }
3658  if ( properties.contains( "coordinate_mode" ) )
3659  {
3660  mode = static_cast< FillCoordinateMode >( properties["coordinate_mode"].toInt() );
3661  }
3662  if ( properties.contains( "alpha" ) )
3663  {
3664  alpha = properties["alpha"].toDouble();
3665  }
3666  if ( properties.contains( "offset" ) )
3667  {
3668  offset = QgsSymbolLayerV2Utils::decodePoint( properties["offset"] );
3669  }
3670  if ( properties.contains( "angle" ) )
3671  {
3672  angle = properties["angle"].toDouble();
3673  }
3674  if ( properties.contains( "width" ) )
3675  {
3676  width = properties["width"].toDouble();
3677  }
3678  QgsRasterFillSymbolLayer* symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
3679  symbolLayer->setCoordinateMode( mode );
3680  symbolLayer->setAlpha( alpha );
3681  symbolLayer->setOffset( offset );
3682  symbolLayer->setAngle( angle );
3683  symbolLayer->setWidth( width );
3684  if ( properties.contains( "offset_unit" ) )
3685  {
3686  symbolLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
3687  }
3688  if ( properties.contains( "offset_map_unit_scale" ) )
3689  {
3690  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
3691  }
3692  if ( properties.contains( "width_unit" ) )
3693  {
3694  symbolLayer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["width_unit"] ) );
3695  }
3696  if ( properties.contains( "width_map_unit_scale" ) )
3697  {
3698  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["width_map_unit_scale"] ) );
3699  }
3700 
3701  symbolLayer->restoreDataDefinedProperties( properties );
3702 
3703  return symbolLayer;
3704 }
3705 
3707 {
3708  Q_UNUSED( symbol );
3709  return true;
3710 }
3711 
3713 {
3714  return "RasterFill";
3715 }
3716 
3718 {
3719  QPainter* p = context.renderContext().painter();
3720  if ( !p )
3721  {
3722  return;
3723  }
3724 
3725  QPointF offset;
3726  if ( !mOffset.isNull() )
3727  {
3730  p->translate( offset );
3731  }
3732  if ( mCoordinateMode == Feature )
3733  {
3734  QRectF boundingRect = points.boundingRect();
3735  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3736  boundingRect.top() - mBrush.transform().dy() ) );
3737  }
3738 
3739  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3740  if ( !mOffset.isNull() )
3741  {
3742  p->translate( -offset );
3743  }
3744 }
3745 
3747 {
3748  prepareExpressions( context );
3749  applyPattern( mBrush, mImageFilePath, mWidth, mAlpha, context );
3750 }
3751 
3753 {
3754  Q_UNUSED( context );
3755 }
3756 
3758 {
3759  QgsStringMap map;
3760  map["imageFile"] = mImageFilePath;
3761  map["coordinate_mode"] = QString::number( mCoordinateMode );
3762  map["alpha"] = QString::number( mAlpha );
3763  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
3764  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
3765  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
3766  map["angle"] = QString::number( mAngle );
3767  map["width"] = QString::number( mWidth );
3768  map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
3769  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
3770 
3772  return map;
3773 }
3774 
3776 {
3779  sl->setAlpha( mAlpha );
3780  sl->setOffset( mOffset );
3781  sl->setOffsetUnit( mOffsetUnit );
3783  sl->setAngle( mAngle );
3784  sl->setWidth( mWidth );
3785  sl->setWidthUnit( mWidthUnit );
3788  copyPaintEffect( sl );
3789  return sl;
3790 }
3791 
3793 {
3794  return mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
3795 }
3796 
3798 {
3799  mImageFilePath = imagePath;
3800 }
3801 
3803 {
3804  mCoordinateMode = mode;
3805 }
3806 
3808 {
3809  mAlpha = alpha;
3810 }
3811 
3813 {
3814  if ( !hasDataDefinedProperties() )
3815  return; // shortcut
3816 
3817  bool hasWidthExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH );
3818  bool hasFileExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILE );
3819  bool hasAlphaExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ALPHA );
3820  bool hasAngleExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE );
3821 
3822  if ( !hasWidthExpression && !hasAngleExpression && !hasAlphaExpression && !hasFileExpression )
3823  {
3824  return; //no data defined settings
3825  }
3826 
3827  bool ok;
3828  if ( hasAngleExpression )
3829  {
3830  context.setOriginalValueVariable( mAngle );
3831  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
3832  if ( ok )
3833  mNextAngle = nextAngle;
3834  }
3835 
3836  if ( !hasWidthExpression && !hasAlphaExpression && !hasFileExpression )
3837  {
3838  return; //nothing further to do
3839  }
3840 
3841  double width = mWidth;
3842  if ( hasWidthExpression )
3843  {
3844  context.setOriginalValueVariable( mWidth );
3846  }
3847  double alpha = mAlpha;
3848  if ( hasAlphaExpression )
3849  {
3850  context.setOriginalValueVariable( mAlpha );
3852  }
3853  QString file = mImageFilePath;
3854  if ( hasFileExpression )
3855  {
3858  }
3859  applyPattern( mBrush, file, width, alpha, context );
3860 }
3861 
3862 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolV2RenderContext &context )
3863 {
3864  QImage image( imageFilePath );
3865  if ( image.isNull() )
3866  {
3867  return;
3868  }
3869  if ( !image.hasAlphaChannel() )
3870  {
3871  image = image.convertToFormat( QImage::Format_ARGB32 );
3872  }
3873 
3874  double pixelWidth;
3875  if ( width > 0 )
3876  {
3878  }
3879  else
3880  {
3881  pixelWidth = image.width();
3882  }
3883 
3884  //reduce alpha of image
3885  if ( alpha < 1.0 )
3886  {
3887  QPainter p;
3888  p.begin( &image );
3889  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3890  QColor alphaColor( 0, 0, 0 );
3891  alphaColor.setAlphaF( alpha );
3892  p.fillRect( image.rect(), alphaColor );
3893  p.end();
3894  }
3895 
3896  //resize image if required
3897  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3898  {
3899  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3900  }
3901 
3902  brush.setTextureImage( image );
3903 }
virtual QSet< QString > usedAttributes() const
Returns the set of attributes referenced by the layer.
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
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
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
bool ignoreRings() const
Returns whether the shapeburst fill is set to ignore polygon interior rings.
void setForceVectorOutput(bool force)
QgsSymbolV2::OutputUnit intervalUnit() const
QgsSymbolV2::OutputUnit patternWidthUnit() const
void setDistanceUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit for the maximum distance to shade inside of the shape from the polygon&#39;s boundary...
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
QImage convertToFormat(Format format, QFlags< Qt::ImageConversionFlag > flags) const
const QgsMapUnitScale & patternWidthMapUnitScale() 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
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].
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
QString ogrFeatureStyleWidth(double widthScaleFactor) 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
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)
QString svgFilePath() const
void stopRender(QgsSymbolV2RenderContext &context) override
void append(const T &value)
qreal dx() const
qreal dy() const
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
SymbolType type() const
Definition: qgssymbolv2.h:107
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)
QColor selectionColor() const
QMap< Key, T > & unite(const QMap< Key, T > &other)
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
void setOutlineWidthMapUnitScale(const QgsMapUnitScale &scale)
double dxfAngle(QgsSymbolV2RenderContext &context) const override
get angle
#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.
void setMatrix(const QMatrix &matrix)
bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
double svgOutlineWidth() const
void setColorAt(qreal position, const QColor &color)
QgsAbstractGeometryV2 * geometry() const
Returns the underlying geometry store.
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
double rendererScale() const
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)
QgsSymbolV2::OutputUnit svgOutlineWidthUnit() const
void scale(qreal sx, qreal sy)
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:96
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
void setTextureImage(const QImage &image)
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.
const QgsMapUnitScale & intervalMapUnitScale() const
void save()
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
bool hasAlphaChannel() const
QgsMapUnitScale mapUnitScale() 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.
static const QString EXPR_COORDINATE_MODE
GradientCoordinateMode coordinateMode() const
Coordinate mode for gradient.
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 copyPaintEffect(QgsSymbolLayerV2 *destLayer) const
Copies paint effect of this layer to another symbol layer.
void setDistanceXMapUnitScale(const QgsMapUnitScale &scale)
static const bool selectionIsOpaque
int blurRadius() const
Returns the blur radius, which controls the amount of blurring applied to the fill.
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
virtual bool hasDataDefinedProperty(const QString &property) const
Checks whether the layer has a matching data defined property and if that property is currently activ...
void applyDataDefinedSettings(QgsSymbolV2RenderContext &context) override
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
double scaleFactor() const
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.
virtual QColor fillColor() const
Get 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.
static double pixelSizeScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns scale factor painter units -> pixel dimensions.
void setWidth(double width)
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
virtual double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QgsSymbolV2::OutputUnit outputUnit() const override
void setSvgOutlineColor(const QColor &c)
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)
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasOutlineParam, QColor &defaultOutlineColor, bool &hasOutlineWidthParam, double &defaultOutlineWidth) const
Tests if an svg file contains parameters for fill, outline color, outline width.
static 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
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:372
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.
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.
Qt::PenStyle borderStyle() const
qreal x() const
qreal y() const
void append(const T &value)
void setOutlineWidthUnit(QgsSymbolV2::OutputUnit unit)
QgsLineSymbolV2 * mOutline
Custom outline.
static const QString EXPR_SPREAD
#define DEFAULT_SIMPLEFILL_STYLE
double width() const
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.
void setTransform(const QTransform &matrix)
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:384
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
double mOffset
Offset perpendicular to line direction.
void fill(uint pixelValue)
static QString encodePoint(QPointF point)
void setReferencePoint1IsCentroid(bool isCentroid)
Sets the starting point of the gradient to be the feature centroid.
QByteArray getImageData(const QString &path) const
Get