QGIS API Documentation
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 
952  mBlurRadius( blurRadius ),
953  mUseWholeShape( useWholeShape ),
954  mMaxDistance( maxDistance ),
955  mDistanceUnit( QgsSymbolV2::MM ),
956  mColorType( colorType ),
957  mColor2( color2 ),
958  mGradientRamp( nullptr ),
959  mTwoColorGradientRamp( nullptr ),
960  mIgnoreRings( false ),
961  mOffsetUnit( QgsSymbolV2::MM )
962 {
963  mColor = color;
964 }
965 
967 {
968  delete mGradientRamp;
969 }
970 
972 {
973  //default to a two-color gradient
975  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
976  int blurRadius = 0;
977  bool useWholeShape = true;
978  double maxDistance = 5;
979  QPointF offset;
980 
981  //update fill properties from props
982  if ( props.contains( "color_type" ) )
983  {
984  colorType = static_cast< ShapeburstColorType >( props["color_type"].toInt() );
985  }
986  if ( props.contains( "shapeburst_color" ) )
987  {
988  //pre 2.5 projects used "shapeburst_color"
989  color = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color"] );
990  }
991  else if ( props.contains( "color" ) )
992  {
993  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
994  }
995 
996  if ( props.contains( "shapeburst_color2" ) )
997  {
998  //pre 2.5 projects used "shapeburst_color2"
999  color2 = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color2"] );
1000  }
1001  else if ( props.contains( "gradient_color2" ) )
1002  {
1003  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
1004  }
1005  if ( props.contains( "blur_radius" ) )
1006  {
1007  blurRadius = props["blur_radius"].toInt();
1008  }
1009  if ( props.contains( "use_whole_shape" ) )
1010  {
1011  useWholeShape = props["use_whole_shape"].toInt();
1012  }
1013  if ( props.contains( "max_distance" ) )
1014  {
1015  maxDistance = props["max_distance"].toDouble();
1016  }
1017  if ( props.contains( "offset" ) )
1018  {
1019  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
1020  }
1021 
1022  //attempt to create color ramp from props
1024 
1025  //create a new shapeburst fill layer with desired properties
1026  QgsShapeburstFillSymbolLayerV2* sl = new QgsShapeburstFillSymbolLayerV2( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1027  sl->setOffset( offset );
1028  if ( props.contains( "offset_unit" ) )
1029  {
1030  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
1031  }
1032  if ( props.contains( "distance_unit" ) )
1033  {
1034  sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
1035  }
1036  if ( props.contains( "offset_map_unit_scale" ) )
1037  {
1038  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
1039  }
1040  if ( props.contains( "distance_map_unit_scale" ) )
1041  {
1042  sl->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["distance_map_unit_scale"] ) );
1043  }
1044  if ( props.contains( "ignore_rings" ) )
1045  {
1046  sl->setIgnoreRings( props["ignore_rings"].toInt() );
1047  }
1048  if ( gradientRamp )
1049  {
1050  sl->setColorRamp( gradientRamp );
1051  }
1052 
1053  sl->restoreDataDefinedProperties( props );
1054 
1055  return sl;
1056 }
1057 
1059 {
1060  return "ShapeburstFill";
1061 }
1062 
1064 {
1065  delete mGradientRamp;
1066  mGradientRamp = ramp;
1067 }
1068 
1069 void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
1070  double& maxDistance, bool& ignoreRings )
1071 {
1072  bool ok;
1073 
1074  //first gradient color
1075  color = mColor;
1077  {
1080  if ( ok )
1081  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
1082  }
1083 
1084  //second gradient color
1085  color2 = mColor2;
1087  {
1090  if ( ok )
1091  color2 = QgsSymbolLayerV2Utils::decodeColor( colorString );
1092  }
1093 
1094  //blur radius
1097  {
1100  }
1101 
1102  //use whole shape
1105  {
1108  }
1109 
1110  //max distance
1113  {
1116  }
1117 
1118  //ignore rings
1119  ignoreRings = mIgnoreRings;
1121  {
1124  }
1125 
1126 }
1127 
1129 {
1130  //TODO - check this
1131  QColor selColor = context.renderContext().selectionColor();
1132  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
1133  mSelBrush = QBrush( selColor );
1134 
1135  prepareExpressions( context );
1136 }
1137 
1139 {
1140  Q_UNUSED( context );
1141 }
1142 
1144 {
1145  QPainter* p = context.renderContext().painter();
1146  if ( !p )
1147  {
1148  return;
1149  }
1150 
1151  if ( context.selected() )
1152  {
1153  //feature is selected, draw using selection style
1154  p->setBrush( mSelBrush );
1155  QPointF offset;
1156  if ( !mOffset.isNull() )
1157  {
1160  p->translate( offset );
1161  }
1162  _renderPolygon( p, points, rings, context );
1163  if ( !mOffset.isNull() )
1164  {
1165  p->translate( -offset );
1166  }
1167  return;
1168  }
1169 
1170  QColor color1, color2;
1171  int blurRadius;
1172  bool useWholeShape;
1173  double maxDistance;
1174  bool ignoreRings;
1175  //calculate data defined symbology
1176  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1177 
1178  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1179  int outputPixelMaxDist = 0;
1180  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1181  {
1182  //convert max distance to pixels
1183  const QgsRenderContext& ctx = context.renderContext();
1184  outputPixelMaxDist = maxDistance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit, mDistanceMapUnitScale );
1185  }
1186 
1187  //if we are using the two color mode, create a gradient ramp
1189  {
1190  mTwoColorGradientRamp = new QgsVectorGradientColorRampV2( color1, color2 );
1191  }
1192 
1193  //no border for shapeburst fills
1194  p->setPen( QPen( Qt::NoPen ) );
1195 
1196  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1197  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1198  //create a QImage to draw shapeburst in
1199  double imWidth = points.boundingRect().width() + ( sideBuffer * 2 );
1200  double imHeight = points.boundingRect().height() + ( sideBuffer * 2 );
1201  QImage * fillImage = new QImage( imWidth * context.renderContext().rasterScaleFactor(),
1202  imHeight * context.renderContext().rasterScaleFactor(), QImage::Format_ARGB32_Premultiplied );
1203  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1204  //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
1205  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1206  fillImage->fill( Qt::black );
1207 
1208  //also create an image to store the alpha channel
1209  QImage * alphaImage = new QImage( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1210  //initially fill the alpha channel image with a transparent color
1211  alphaImage->fill( Qt::transparent );
1212 
1213  //now, draw the polygon in the alpha channel image
1214  QPainter imgPainter;
1215  imgPainter.begin( alphaImage );
1216  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1217  imgPainter.setBrush( QBrush( Qt::white ) );
1218  imgPainter.setPen( QPen( Qt::black ) );
1219  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1220  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1221  _renderPolygon( &imgPainter, points, rings, context );
1222  imgPainter.end();
1223 
1224  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1225  //(this avoids calling _renderPolygon twice, since that can be slow)
1226  imgPainter.begin( fillImage );
1227  if ( !ignoreRings )
1228  {
1229  imgPainter.drawImage( 0, 0, *alphaImage );
1230  }
1231  else
1232  {
1233  //using ignore rings mode, so the alpha image can't be used
1234  //directly as the alpha channel contains polygon rings and we need
1235  //to draw now without any rings
1236  imgPainter.setBrush( QBrush( Qt::white ) );
1237  imgPainter.setPen( QPen( Qt::black ) );
1238  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1239  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1240  _renderPolygon( &imgPainter, points, nullptr, context );
1241  }
1242  imgPainter.end();
1243 
1244  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1245  double * dtArray = distanceTransform( fillImage );
1246 
1247  //copy distance transform values back to QImage, shading by appropriate color ramp
1249  context.alpha(), useWholeShape, outputPixelMaxDist );
1250 
1251  //clean up some variables
1252  delete [] dtArray;
1254  {
1255  delete mTwoColorGradientRamp;
1256  }
1257 
1258  //apply blur if desired
1259  if ( blurRadius > 0 )
1260  {
1261  QgsSymbolLayerV2Utils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1262  }
1263 
1264  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1265  imgPainter.begin( fillImage );
1266  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1267  imgPainter.drawImage( 0, 0, *alphaImage );
1268  imgPainter.end();
1269  //we're finished with the alpha channel image now
1270  delete alphaImage;
1271 
1272  //draw shapeburst image in correct place in the destination painter
1273 
1274  p->save();
1275  QPointF offset;
1276  if ( !mOffset.isNull() )
1277  {
1280  p->translate( offset );
1281  }
1282 
1283  p->scale( 1 / context.renderContext().rasterScaleFactor(), 1 / context.renderContext().rasterScaleFactor() );
1284  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1285 
1286  delete fillImage;
1287 
1288  if ( !mOffset.isNull() )
1289  {
1290  p->translate( -offset );
1291  }
1292  p->restore();
1293 
1294 }
1295 
1296 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1297 
1298 /* distance transform of a 1d function using squared distance */
1299 void QgsShapeburstFillSymbolLayerV2::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1300 {
1301  int k = 0;
1302  v[0] = 0;
1303  z[0] = -INF;
1304  z[1] = + INF;
1305  for ( int q = 1; q <= n - 1; q++ )
1306  {
1307  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1308  while ( s <= z[k] )
1309  {
1310  k--;
1311  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1312  }
1313  k++;
1314  v[k] = q;
1315  z[k] = s;
1316  z[k+1] = + INF;
1317  }
1318 
1319  k = 0;
1320  for ( int q = 0; q <= n - 1; q++ )
1321  {
1322  while ( z[k+1] < q )
1323  k++;
1324  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1325  }
1326 }
1327 
1328 /* distance transform of 2d function using squared distance */
1329 void QgsShapeburstFillSymbolLayerV2::distanceTransform2d( double * im, int width, int height )
1330 {
1331  int maxDimension = qMax( width, height );
1332  double *f = new double[ maxDimension ];
1333  int *v = new int[ maxDimension ];
1334  double *z = new double[ maxDimension + 1 ];
1335  double *d = new double[ maxDimension ];
1336 
1337  // transform along columns
1338  for ( int x = 0; x < width; x++ )
1339  {
1340  for ( int y = 0; y < height; y++ )
1341  {
1342  f[y] = im[ x + y * width ];
1343  }
1344  distanceTransform1d( f, height, v, z, d );
1345  for ( int y = 0; y < height; y++ )
1346  {
1347  im[ x + y * width ] = d[y];
1348  }
1349  }
1350 
1351  // transform along rows
1352  for ( int y = 0; y < height; y++ )
1353  {
1354  for ( int x = 0; x < width; x++ )
1355  {
1356  f[x] = im[ x + y*width ];
1357  }
1358  distanceTransform1d( f, width, v, z, d );
1359  for ( int x = 0; x < width; x++ )
1360  {
1361  im[ x + y*width ] = d[x];
1362  }
1363  }
1364 
1365  delete [] d;
1366  delete [] f;
1367  delete [] v;
1368  delete [] z;
1369 }
1370 
1371 /* distance transform of a binary QImage */
1372 double * QgsShapeburstFillSymbolLayerV2::distanceTransform( QImage *im )
1373 {
1374  int width = im->width();
1375  int height = im->height();
1376 
1377  double * dtArray = new double[width * height];
1378 
1379  //load qImage to array
1380  QRgb tmpRgb;
1381  int idx = 0;
1382  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1383  {
1384  const QRgb* scanLine = reinterpret_cast< const QRgb* >( im->constScanLine( heightIndex ) );
1385  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1386  {
1387  tmpRgb = scanLine[widthIndex];
1388  if ( qRed( tmpRgb ) == 0 )
1389  {
1390  //black pixel, so zero distance
1391  dtArray[ idx ] = 0;
1392  }
1393  else
1394  {
1395  //white pixel, so initially set distance as infinite
1396  dtArray[ idx ] = INF;
1397  }
1398  idx++;
1399  }
1400  }
1401 
1402  //calculate squared distance transform
1403  distanceTransform2d( dtArray, width, height );
1404 
1405  return dtArray;
1406 }
1407 
1408 void QgsShapeburstFillSymbolLayerV2::dtArrayToQImage( double * array, QImage *im, QgsVectorColorRampV2* ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1409 {
1410  int width = im->width();
1411  int height = im->height();
1412 
1413  //find maximum distance value
1414  double maxDistanceValue;
1415 
1416  if ( useWholeShape )
1417  {
1418  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1419  double dtMaxValue = array[0];
1420  for ( int i = 1; i < ( width * height ); ++i )
1421  {
1422  if ( array[i] > dtMaxValue )
1423  {
1424  dtMaxValue = array[i];
1425  }
1426  }
1427 
1428  //values in distance transform are squared
1429  maxDistanceValue = sqrt( dtMaxValue );
1430  }
1431  else
1432  {
1433  //use max distance set in symbol properties
1434  maxDistanceValue = maxPixelDistance;
1435  }
1436 
1437  //update the pixels in the provided QImage
1438  int idx = 0;
1439  double squaredVal = 0;
1440  double pixVal = 0;
1441  QColor pixColor;
1442  bool layerHasAlpha = layerAlpha < 1.0;
1443 
1444  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1445  {
1446  QRgb* scanLine = reinterpret_cast< QRgb* >( im->scanLine( heightIndex ) );
1447  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1448  {
1449  //result of distance transform
1450  squaredVal = array[idx];
1451 
1452  //scale result to fit in the range [0, 1]
1453  if ( maxDistanceValue > 0 )
1454  {
1455  pixVal = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1456  }
1457  else
1458  {
1459  pixVal = 1.0;
1460  }
1461 
1462  //convert value to color from ramp
1463  pixColor = ramp->color( pixVal );
1464 
1465  int pixAlpha = pixColor.alpha();
1466  if (( layerHasAlpha ) || ( pixAlpha != 255 ) )
1467  {
1468  //apply layer's transparency to alpha value
1469  double alpha = pixAlpha * layerAlpha;
1470  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1471  QgsSymbolLayerV2Utils::premultiplyColor( pixColor, alpha );
1472  }
1473 
1474  scanLine[widthIndex] = pixColor.rgba();
1475  idx++;
1476  }
1477  }
1478 }
1479 
1481 {
1482  QgsStringMap map;
1483  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1484  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
1485  map["color_type"] = QString::number( mColorType );
1486  map["blur_radius"] = QString::number( mBlurRadius );
1487  map["use_whole_shape"] = QString::number( mUseWholeShape );
1488  map["max_distance"] = QString::number( mMaxDistance );
1489  map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1490  map["distance_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale );
1491  map["ignore_rings"] = QString::number( mIgnoreRings );
1492  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1493  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1494  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1495 
1497 
1498  if ( mGradientRamp )
1499  {
1500  map.unite( mGradientRamp->properties() );
1501  }
1502 
1503  return map;
1504 }
1505 
1507 {
1509  if ( mGradientRamp )
1510  {
1511  sl->setColorRamp( mGradientRamp->clone() );
1512  }
1516  sl->setOffset( mOffset );
1517  sl->setOffsetUnit( mOffsetUnit );
1520  copyPaintEffect( sl );
1521  return sl;
1522 }
1523 
1525 {
1526  double offsetBleed = qMax( mOffset.x(), mOffset.y() );
1527  return offsetBleed;
1528 }
1529 
1531 {
1532  mDistanceUnit = unit;
1533  mOffsetUnit = unit;
1534 }
1535 
1537 {
1538  if ( mDistanceUnit == mOffsetUnit )
1539  {
1540  return mDistanceUnit;
1541  }
1542  return QgsSymbolV2::Mixed;
1543 }
1544 
1546 {
1547  mDistanceMapUnitScale = scale;
1548  mOffsetMapUnitScale = scale;
1549 }
1550 
1552 {
1554  {
1555  return mDistanceMapUnitScale;
1556  }
1557  return QgsMapUnitScale();
1558 }
1559 
1560 
1561 //QgsImageFillSymbolLayer
1562 
1564  : mNextAngle( 0.0 )
1565  , mOutlineWidth( 0.0 )
1566  , mOutlineWidthUnit( QgsSymbolV2::MM )
1567  , mOutline( nullptr )
1568 {
1569  setSubSymbol( new QgsLineSymbolV2() );
1570 }
1571 
1573 {
1574 }
1575 
1577 {
1578  QPainter* p = context.renderContext().painter();
1579  if ( !p )
1580  {
1581  return;
1582  }
1583 
1584  mNextAngle = mAngle;
1585  applyDataDefinedSettings( context );
1586 
1587  p->setPen( QPen( Qt::NoPen ) );
1588 
1589  QTransform bkTransform = mBrush.transform();
1591  {
1592  //transform brush to upper left corner of geometry bbox
1593  QPointF leftCorner = points.boundingRect().topLeft();
1594  QTransform t = mBrush.transform();
1595  t.translate( leftCorner.x(), leftCorner.y() );
1596  mBrush.setTransform( t );
1597  }
1598 
1599  if ( context.selected() )
1600  {
1601  QColor selColor = context.renderContext().selectionColor();
1602  // Alister - this doesn't seem to work here
1603  //if ( ! selectionIsOpaque )
1604  // selColor.setAlphaF( context.alpha() );
1605  p->setBrush( QBrush( selColor ) );
1606  _renderPolygon( p, points, rings, context );
1607  }
1608 
1609  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1610  {
1611  QTransform t = mBrush.transform();
1612  t.rotate( mNextAngle );
1613  mBrush.setTransform( t );
1614  }
1615  p->setBrush( mBrush );
1616  _renderPolygon( p, points, rings, context );
1617  if ( mOutline )
1618  {
1619  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1620  if ( rings )
1621  {
1622  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1623  for ( ; ringIt != rings->constEnd(); ++ringIt )
1624  {
1625  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1626  }
1627  }
1628  }
1629 
1630  mBrush.setTransform( bkTransform );
1631 }
1632 
1634 {
1635  if ( !symbol ) //unset current outline
1636  {
1637  delete mOutline;
1638  mOutline = nullptr;
1639  return true;
1640  }
1641 
1642  if ( symbol->type() != QgsSymbolV2::Line )
1643  {
1644  delete symbol;
1645  return false;
1646  }
1647 
1648  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1649  if ( lineSymbol )
1650  {
1651  delete mOutline;
1652  mOutline = lineSymbol;
1653  return true;
1654  }
1655 
1656  delete symbol;
1657  return false;
1658 }
1659 
1661 {
1662  mOutlineWidthUnit = unit;
1663 }
1664 
1666 {
1667  return mOutlineWidthUnit;
1668 }
1669 
1671 {
1672  mOutlineWidthMapUnitScale = scale;
1673 }
1674 
1676 {
1678 }
1679 
1681 {
1682  if ( mOutline && mOutline->symbolLayer( 0 ) )
1683  {
1684  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
1685  return subLayerBleed;
1686  }
1687  return 0;
1688 }
1689 
1691 {
1692  double width = mOutlineWidth;
1694  {
1697  }
1699 }
1700 
1702 {
1703  Q_UNUSED( context );
1704  if ( !mOutline )
1705  {
1706  return QColor( Qt::black );
1707  }
1708  return mOutline->color();
1709 }
1710 
1712 {
1713  return Qt::SolidLine;
1714 #if 0
1715  if ( !mOutline )
1716  {
1717  return Qt::SolidLine;
1718  }
1719  else
1720  {
1721  return mOutline->dxfPenStyle();
1722  }
1723 #endif //0
1724 }
1725 
1727 {
1729  if ( mOutline )
1730  attr.unite( mOutline->usedAttributes() );
1731  return attr;
1732 }
1733 
1734 
1735 //QgsSVGFillSymbolLayer
1736 
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 
1753  mPatternWidth( width ),
1755  mSvgData( svgData ),
1757 {
1758  storeViewBox();
1759  mOutlineWidth = 0.3;
1760  mAngle = angle;
1761  mColor = QColor( 255, 255, 255 );
1762  mSvgOutlineColor = QColor( 0, 0, 0 );
1763  mSvgOutlineWidth = 0.2;
1764  setSubSymbol( new QgsLineSymbolV2() );
1765  setDefaultSvgParams();
1766  mSvgPattern = nullptr;
1767 }
1768 
1770 {
1771  delete mSvgPattern;
1772 }
1773 
1775 {
1777  mPatternWidthUnit = unit;
1778  mSvgOutlineWidthUnit = unit;
1779  mOutlineWidthUnit = unit;
1780 }
1781 
1783 {
1785  if ( mPatternWidthUnit != unit || mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
1786  {
1787  return QgsSymbolV2::Mixed;
1788  }
1789  return unit;
1790 }
1791 
1793 {
1795  mPatternWidthMapUnitScale = scale;
1797  mOutlineWidthMapUnitScale = scale;
1798 }
1799 
1801 {
1805  {
1807  }
1808  return QgsMapUnitScale();
1809 }
1810 
1812 {
1814  storeViewBox();
1815 
1816  mSvgFilePath = svgPath;
1817  setDefaultSvgParams();
1818 }
1819 
1821 {
1822  QByteArray data;
1823  double width = 20;
1825  double angle = 0.0;
1826 
1827  if ( properties.contains( "width" ) )
1828  {
1829  width = properties["width"].toDouble();
1830  }
1831  if ( properties.contains( "svgFile" ) )
1832  {
1833  QString svgName = properties["svgFile"];
1834  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
1835  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
1836  }
1837  if ( properties.contains( "angle" ) )
1838  {
1839  angle = properties["angle"].toDouble();
1840  }
1841 
1842  QgsSVGFillSymbolLayer* symbolLayer = nullptr;
1843  if ( !svgFilePath.isEmpty() )
1844  {
1845  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1846  }
1847  else
1848  {
1849  if ( properties.contains( "data" ) )
1850  {
1851  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1852  }
1853  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1854  }
1855 
1856  //svg parameters
1857  if ( properties.contains( "svgFillColor" ) )
1858  {
1859  //pre 2.5 projects used "svgFillColor"
1860  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgFillColor"] ) );
1861  }
1862  else if ( properties.contains( "color" ) )
1863  {
1864  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["color"] ) );
1865  }
1866  if ( properties.contains( "svgOutlineColor" ) )
1867  {
1868  //pre 2.5 projects used "svgOutlineColor"
1869  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgOutlineColor"] ) );
1870  }
1871  else if ( properties.contains( "outline_color" ) )
1872  {
1873  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
1874  }
1875  else if ( properties.contains( "line_color" ) )
1876  {
1877  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] ) );
1878  }
1879  if ( properties.contains( "svgOutlineWidth" ) )
1880  {
1881  //pre 2.5 projects used "svgOutlineWidth"
1882  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1883  }
1884  else if ( properties.contains( "outline_width" ) )
1885  {
1886  symbolLayer->setSvgOutlineWidth( properties["outline_width"].toDouble() );
1887  }
1888  else if ( properties.contains( "line_width" ) )
1889  {
1890  symbolLayer->setSvgOutlineWidth( properties["line_width"].toDouble() );
1891  }
1892 
1893  //units
1894  if ( properties.contains( "pattern_width_unit" ) )
1895  {
1896  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1897  }
1898  if ( properties.contains( "pattern_width_map_unit_scale" ) )
1899  {
1900  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["pattern_width_map_unit_scale"] ) );
1901  }
1902  if ( properties.contains( "svg_outline_width_unit" ) )
1903  {
1904  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1905  }
1906  if ( properties.contains( "svg_outline_width_map_unit_scale" ) )
1907  {
1908  symbolLayer->setSvgOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["svg_outline_width_map_unit_scale"] ) );
1909  }
1910  if ( properties.contains( "outline_width_unit" ) )
1911  {
1912  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1913  }
1914  if ( properties.contains( "outline_width_map_unit_scale" ) )
1915  {
1916  symbolLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
1917  }
1918 
1919  symbolLayer->restoreDataDefinedProperties( properties );
1920 
1921  return symbolLayer;
1922 }
1923 
1925 {
1926  return "SVGFill";
1927 }
1928 
1929 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1930  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1933 {
1934  if ( mSvgViewBox.isNull() )
1935  {
1936  return;
1937  }
1938 
1939  delete mSvgPattern;
1940  mSvgPattern = nullptr;
1942 
1943  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1944  {
1945  mSvgPattern = new QImage();
1946  brush.setTextureImage( *mSvgPattern );
1947  }
1948  else
1949  {
1950  bool fitsInCache = true;
1952  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1953  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1954  if ( !fitsInCache )
1955  {
1956  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1957  context.renderContext().scaleFactor(), 1.0 );
1958  double hwRatio = 1.0;
1959  if ( patternPict.width() > 0 )
1960  {
1961  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1962  }
1963  mSvgPattern = new QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1964  mSvgPattern->fill( 0 ); // transparent background
1965 
1966  QPainter p( mSvgPattern );
1967  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1968  }
1969 
1970  QTransform brushTransform;
1971  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1972  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1973  {
1974  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1975  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1976  brush.setTextureImage( transparentImage );
1977  }
1978  else
1979  {
1980  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1981  }
1982  brush.setTransform( brushTransform );
1983  }
1984 }
1985 
1987 {
1988 
1990 
1991  if ( mOutline )
1992  {
1993  mOutline->startRender( context.renderContext(), context.fields() );
1994  }
1995 
1996  prepareExpressions( context );
1997 }
1998 
2000 {
2001  if ( mOutline )
2002  {
2003  mOutline->stopRender( context.renderContext() );
2004  }
2005 }
2006 
2008 {
2009  QgsStringMap map;
2010  if ( !mSvgFilePath.isEmpty() )
2011  {
2013  }
2014  else
2015  {
2016  map.insert( "data", QString( mSvgData.toHex() ) );
2017  }
2018 
2019  map.insert( "width", QString::number( mPatternWidth ) );
2020  map.insert( "angle", QString::number( mAngle ) );
2021 
2022  //svg parameters
2024  map.insert( "outline_color", QgsSymbolLayerV2Utils::encodeColor( mSvgOutlineColor ) );
2025  map.insert( "outline_width", QString::number( mSvgOutlineWidth ) );
2026 
2027  //units
2028  map.insert( "pattern_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit ) );
2029  map.insert( "pattern_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2030  map.insert( "svg_outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit ) );
2031  map.insert( "svg_outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mSvgOutlineWidthMapUnitScale ) );
2032  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2033  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2034 
2036  return map;
2037 }
2038 
2040 {
2041  QgsSVGFillSymbolLayer* clonedLayer = nullptr;
2042  if ( !mSvgFilePath.isEmpty() )
2043  {
2044  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
2045  clonedLayer->setSvgFillColor( mColor );
2046  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
2047  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
2048  }
2049  else
2050  {
2051  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
2052  }
2053 
2054  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2058  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
2060 
2061  if ( mOutline )
2062  {
2063  clonedLayer->setSubSymbol( mOutline->clone() );
2064  }
2065  copyDataDefinedProperties( clonedLayer );
2066  copyPaintEffect( clonedLayer );
2067  return clonedLayer;
2068 }
2069 
2070 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
2071 {
2072  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2073  if ( !props.value( "uom", "" ).isEmpty() )
2074  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2075  element.appendChild( symbolizerElem );
2076 
2077  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2078 
2079  QDomElement fillElem = doc.createElement( "se:Fill" );
2080  symbolizerElem.appendChild( fillElem );
2081 
2082  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2083  fillElem.appendChild( graphicFillElem );
2084 
2085  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2086  graphicFillElem.appendChild( graphicElem );
2087 
2088  if ( !mSvgFilePath.isEmpty() )
2089  {
2090  QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mSvgFilePath, "image/svg+xml", mColor, mPatternWidth );
2091  }
2092  else
2093  {
2094  // TODO: create svg from data
2095  // <se:InlineContent>
2096  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
2097  }
2098 
2099  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
2100  {
2101  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
2102  }
2103 
2104  // <Rotation>
2105  QString angleFunc;
2106  bool ok;
2107  double angle = props.value( "angle", "0" ).toDouble( &ok );
2108  if ( !ok )
2109  {
2110  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
2111  }
2112  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2113  {
2114  angleFunc = QString::number( angle + mAngle );
2115  }
2116  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2117 
2118  if ( mOutline )
2119  {
2120  // the outline sub symbol should be stored within the Stroke element,
2121  // but it will be stored in a separated LineSymbolizer because it could
2122  // have more than one layer
2123  mOutline->toSld( doc, element, props );
2124  }
2125 }
2126 
2128 {
2129  QgsDebugMsg( "Entered." );
2130 
2131  QString path, mimeType;
2132  QColor fillColor, borderColor;
2133  Qt::PenStyle penStyle;
2134  double size, borderWidth;
2135 
2136  QDomElement fillElem = element.firstChildElement( "Fill" );
2137  if ( fillElem.isNull() )
2138  return nullptr;
2139 
2140  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2141  if ( graphicFillElem.isNull() )
2142  return nullptr;
2143 
2144  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2145  if ( graphicElem.isNull() )
2146  return nullptr;
2147 
2148  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2149  return nullptr;
2150 
2151  if ( mimeType != "image/svg+xml" )
2152  return nullptr;
2153 
2154  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
2155 
2156  double angle = 0.0;
2157  QString angleFunc;
2158  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2159  {
2160  bool ok;
2161  double d = angleFunc.toDouble( &ok );
2162  if ( ok )
2163  angle = d;
2164  }
2165 
2166  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
2167  sl->setSvgFillColor( fillColor );
2168  sl->setSvgOutlineColor( borderColor );
2169  sl->setSvgOutlineWidth( borderWidth );
2170 
2171  // try to get the outline
2172  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2173  if ( !strokeElem.isNull() )
2174  {
2176  if ( l )
2177  {
2178  QgsSymbolLayerV2List layers;
2179  layers.append( l );
2180  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2181  }
2182  }
2183 
2184  return sl;
2185 }
2186 
2188 {
2192  {
2193  return; //no data defined settings
2194  }
2195 
2196  bool ok;
2197 
2199  {
2200  context.setOriginalValueVariable( mAngle );
2201  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
2202  if ( ok )
2203  mNextAngle = nextAngle;
2204  }
2205 
2206  double width = mPatternWidth;
2208  {
2211  }
2212  QString svgFile = mSvgFilePath;
2214  {
2217  }
2218  QColor svgFillColor = mColor;
2220  {
2223  if ( ok )
2224  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2225  }
2226  QColor svgOutlineColor = mSvgOutlineColor;
2228  {
2231  if ( ok )
2232  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
2233  }
2234  double outlineWidth = mSvgOutlineWidth;
2236  {
2239  }
2240  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
2242 
2243 }
2244 
2245 void QgsSVGFillSymbolLayer::storeViewBox()
2246 {
2247  if ( !mSvgData.isEmpty() )
2248  {
2249  QSvgRenderer r( mSvgData );
2250  if ( r.isValid() )
2251  {
2252  mSvgViewBox = r.viewBoxF();
2253  return;
2254  }
2255  }
2256 
2257  mSvgViewBox = QRectF();
2258  return;
2259 }
2260 
2261 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2262 {
2263  if ( mSvgFilePath.isEmpty() )
2264  {
2265  return;
2266  }
2267 
2268  bool hasFillParam, hasFillOpacityParam, hasOutlineParam, hasOutlineWidthParam, hasOutlineOpacityParam;
2269  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultOutlineColor, hasDefaultOutlineWidth, hasDefaultOutlineOpacity;
2270  QColor defaultFillColor, defaultOutlineColor;
2271  double defaultOutlineWidth, defaultFillOpacity, defaultOutlineOpacity;
2272  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2273  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2274  hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
2275  hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
2276  hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
2277 
2278  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2279  double newOutlineOpacity = hasOutlineOpacityParam ? mSvgOutlineColor.alphaF() : 1.0;
2280 
2281  if ( hasDefaultFillColor )
2282  {
2283  mColor = defaultFillColor;
2284  mColor.setAlphaF( newFillOpacity );
2285  }
2286  if ( hasDefaultFillOpacity )
2287  {
2288  mColor.setAlphaF( defaultFillOpacity );
2289  }
2290  if ( hasDefaultOutlineColor )
2291  {
2292  mSvgOutlineColor = defaultOutlineColor;
2293  mSvgOutlineColor.setAlphaF( newOutlineOpacity );
2294  }
2295  if ( hasDefaultOutlineOpacity )
2296  {
2297  mSvgOutlineColor.setAlphaF( defaultOutlineOpacity );
2298  }
2299  if ( hasDefaultOutlineWidth )
2300  {
2301  mSvgOutlineWidth = defaultOutlineWidth;
2302  }
2303 }
2304 
2305 
2308  , mDistance( 5.0 )
2309  , mDistanceUnit( QgsSymbolV2::MM )
2310  , mLineWidth( 0 )
2311  , mLineWidthUnit( QgsSymbolV2::MM )
2312  , mLineAngle( 45.0 )
2313  , mOffset( 0.0 )
2314  , mOffsetUnit( QgsSymbolV2::MM )
2315  , mFillLineSymbol( nullptr )
2316 {
2317  setSubSymbol( new QgsLineSymbolV2() );
2318  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
2319 }
2320 
2322 {
2323  mFillLineSymbol->setWidth( w );
2324  mLineWidth = w;
2325 }
2326 
2328 {
2329  mFillLineSymbol->setColor( c );
2330  mColor = c;
2331 }
2332 
2334 {
2335  delete mFillLineSymbol;
2336 }
2337 
2339 {
2340  if ( !symbol )
2341  {
2342  return false;
2343  }
2344 
2345  if ( symbol->type() == QgsSymbolV2::Line )
2346  {
2347  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
2348  if ( lineSymbol )
2349  {
2350  delete mFillLineSymbol;
2351  mFillLineSymbol = lineSymbol;
2352 
2353  return true;
2354  }
2355  }
2356  delete symbol;
2357  return false;
2358 }
2359 
2361 {
2362  return mFillLineSymbol;
2363 }
2364 
2366 {
2368  if ( mFillLineSymbol )
2369  attr.unite( mFillLineSymbol->usedAttributes() );
2370  return attr;
2371 }
2372 
2374 {
2375  return 0;
2376 }
2377 
2379 {
2381  mDistanceUnit = unit;
2382  mLineWidthUnit = unit;
2383  mOffsetUnit = unit;
2384 }
2385 
2387 {
2389  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2390  {
2391  return QgsSymbolV2::Mixed;
2392  }
2393  return unit;
2394 }
2395 
2397 {
2399  mDistanceMapUnitScale = scale;
2400  mLineWidthMapUnitScale = scale;
2401  mOffsetMapUnitScale = scale;
2402 }
2403 
2405 {
2409  {
2410  return mDistanceMapUnitScale;
2411  }
2412  return QgsMapUnitScale();
2413 }
2414 
2416 {
2418 
2419  //default values
2420  double lineAngle = 45;
2421  double distance = 5;
2422  double lineWidth = 0.5;
2423  QColor color( Qt::black );
2424  double offset = 0.0;
2425 
2426  if ( properties.contains( "lineangle" ) )
2427  {
2428  //pre 2.5 projects used "lineangle"
2429  lineAngle = properties["lineangle"].toDouble();
2430  }
2431  else if ( properties.contains( "angle" ) )
2432  {
2433  lineAngle = properties["angle"].toDouble();
2434  }
2435  patternLayer->setLineAngle( lineAngle );
2436 
2437  if ( properties.contains( "distance" ) )
2438  {
2439  distance = properties["distance"].toDouble();
2440  }
2441  patternLayer->setDistance( distance );
2442 
2443  if ( properties.contains( "linewidth" ) )
2444  {
2445  //pre 2.5 projects used "linewidth"
2446  lineWidth = properties["linewidth"].toDouble();
2447  }
2448  else if ( properties.contains( "outline_width" ) )
2449  {
2450  lineWidth = properties["outline_width"].toDouble();
2451  }
2452  else if ( properties.contains( "line_width" ) )
2453  {
2454  lineWidth = properties["line_width"].toDouble();
2455  }
2456  patternLayer->setLineWidth( lineWidth );
2457 
2458  if ( properties.contains( "color" ) )
2459  {
2460  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
2461  }
2462  else if ( properties.contains( "outline_color" ) )
2463  {
2464  color = QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] );
2465  }
2466  else if ( properties.contains( "line_color" ) )
2467  {
2468  color = QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] );
2469  }
2470  patternLayer->setColor( color );
2471 
2472  if ( properties.contains( "offset" ) )
2473  {
2474  offset = properties["offset"].toDouble();
2475  }
2476  patternLayer->setOffset( offset );
2477 
2478 
2479  if ( properties.contains( "distance_unit" ) )
2480  {
2481  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
2482  }
2483  if ( properties.contains( "distance_map_unit_scale" ) )
2484  {
2485  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_map_unit_scale"] ) );
2486  }
2487  if ( properties.contains( "line_width_unit" ) )
2488  {
2489  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
2490  }
2491  else if ( properties.contains( "outline_width_unit" ) )
2492  {
2493  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2494  }
2495  if ( properties.contains( "line_width_map_unit_scale" ) )
2496  {
2497  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["line_width_map_unit_scale"] ) );
2498  }
2499  if ( properties.contains( "offset_unit" ) )
2500  {
2501  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
2502  }
2503  if ( properties.contains( "offset_map_unit_scale" ) )
2504  {
2505  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
2506  }
2507  if ( properties.contains( "outline_width_unit" ) )
2508  {
2509  patternLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2510  }
2511  if ( properties.contains( "outline_width_map_unit_scale" ) )
2512  {
2513  patternLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
2514  }
2515 
2516  patternLayer->restoreDataDefinedProperties( properties );
2517 
2518  return patternLayer;
2519 }
2520 
2522 {
2523  return "LinePatternFill";
2524 }
2525 
2526 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
2527  double lineWidth, const QColor& color )
2528 {
2529  Q_UNUSED( lineWidth );
2530  Q_UNUSED( color );
2531 
2532  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2533 
2534  if ( !mFillLineSymbol )
2535  {
2536  return;
2537  }
2538  // We have to make a copy because marker intervals will have to be adjusted
2539  QgsLineSymbolV2* fillLineSymbol = mFillLineSymbol->clone();
2540  if ( !fillLineSymbol )
2541  {
2542  return;
2543  }
2544 
2545  const QgsRenderContext& ctx = context.renderContext();
2546  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2549 
2550  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2551  // For marker lines we have to get markers interval.
2552  double outputPixelBleed = 0;
2553  double outputPixelInterval = 0; // maximum interval
2554  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2555  {
2556  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2557  double layerBleed = layer->estimateMaxBleed();
2558  // TODO: to get real bleed we have to scale it using context and units,
2559  // unfortunately estimateMaxBleed() ignore units completely, e.g.
2560  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
2561  // offset regardless units. This has to be fixed especially
2562  // in estimateMaxBleed(), context probably has to be used.
2563  // For now, we only support millimeters
2564  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
2565  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
2566 
2567  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2568  if ( markerLineLayer )
2569  {
2570  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2571 
2572  // There may be multiple marker lines with different intervals.
2573  // In theory we should find the least common multiple, but that could be too
2574  // big (multiplication of intervals in the worst case).
2575  // Because patterns without small common interval would look strange, we
2576  // believe that the longest interval should usually be sufficient.
2577  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
2578  }
2579  }
2580 
2581  if ( outputPixelInterval > 0 )
2582  {
2583  // We have to adjust marker intervals to integer pixel size to get
2584  // repeatable pattern.
2585  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
2586  outputPixelInterval = qRound( outputPixelInterval );
2587 
2588  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2589  {
2590  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2591 
2592  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2593  if ( markerLineLayer )
2594  {
2595  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2596  }
2597  }
2598  }
2599 
2600  //create image
2601  int height, width;
2602  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2603  {
2604  height = outputPixelDist;
2605  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2606  }
2607  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2608  {
2609  width = outputPixelDist;
2610  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2611  }
2612  else
2613  {
2614  height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2615  width = outputPixelDist / sin( lineAngle * M_PI / 180 );
2616 
2617  // recalculate real angle and distance after rounding to pixels
2618  lineAngle = 180 * atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2619  if ( lineAngle < 0 )
2620  {
2621  lineAngle += 360.;
2622  }
2623 
2624  height = qAbs( height );
2625  width = qAbs( width );
2626 
2627  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
2628 
2629  // Round offset to correspond to one pixel height, otherwise lines may
2630  // be shifted on tile border if offset falls close to pixel center
2631  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
2632  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
2633  }
2634 
2635  //depending on the angle, we might need to render into a larger image and use a subset of it
2636  double dx = 0;
2637  double dy = 0;
2638 
2639  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2640  // thus we add integer multiplications of width and height covering the bleed
2641  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
2642 
2643  // Always buffer at least once so that center of line marker in upper right corner
2644  // does not fall outside due to representation error
2645  bufferMulti = qMax( bufferMulti, 1 );
2646 
2647  int xBuffer = width * bufferMulti;
2648  int yBuffer = height * bufferMulti;
2649  int innerWidth = width;
2650  int innerHeight = height;
2651  width += 2 * xBuffer;
2652  height += 2 * yBuffer;
2653 
2654  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2655  {
2656  return;
2657  }
2658 
2659  QImage patternImage( width, height, QImage::Format_ARGB32 );
2660  patternImage.fill( 0 );
2661 
2662  QPointF p1, p2, p3, p4, p5, p6;
2663  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2664  {
2665  p1 = QPointF( 0, yBuffer );
2666  p2 = QPointF( width, yBuffer );
2667  p3 = QPointF( 0, yBuffer + innerHeight );
2668  p4 = QPointF( width, yBuffer + innerHeight );
2669  }
2670  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2671  {
2672  p1 = QPointF( xBuffer, height );
2673  p2 = QPointF( xBuffer, 0 );
2674  p3 = QPointF( xBuffer + innerWidth, height );
2675  p4 = QPointF( xBuffer + innerWidth, 0 );
2676  }
2677  else if ( lineAngle > 0 && lineAngle < 90 )
2678  {
2679  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2680  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2681  p1 = QPointF( 0, height );
2682  p2 = QPointF( width, 0 );
2683  p3 = QPointF( -dx, height - dy );
2684  p4 = QPointF( width - dx, -dy );
2685  p5 = QPointF( dx, height + dy );
2686  p6 = QPointF( width + dx, dy );
2687  }
2688  else if ( lineAngle > 180 && lineAngle < 270 )
2689  {
2690  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2691  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2692  p1 = QPointF( width, 0 );
2693  p2 = QPointF( 0, height );
2694  p3 = QPointF( width - dx, -dy );
2695  p4 = QPointF( -dx, height - dy );
2696  p5 = QPointF( width + dx, dy );
2697  p6 = QPointF( dx, height + dy );
2698  }
2699  else if ( lineAngle > 90 && lineAngle < 180 )
2700  {
2701  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2702  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2703  p1 = QPointF( 0, 0 );
2704  p2 = QPointF( width, height );
2705  p5 = QPointF( dx, -dy );
2706  p6 = QPointF( width + dx, height - dy );
2707  p3 = QPointF( -dx, dy );
2708  p4 = QPointF( width - dx, height + dy );
2709  }
2710  else if ( lineAngle > 270 && lineAngle < 360 )
2711  {
2712  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2713  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2714  p1 = QPointF( width, height );
2715  p2 = QPointF( 0, 0 );
2716  p5 = QPointF( width + dx, height - dy );
2717  p6 = QPointF( dx, -dy );
2718  p3 = QPointF( width - dx, height + dy );
2719  p4 = QPointF( -dx, dy );
2720  }
2721 
2722  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2723  {
2724  QPointF tempPt;
2725  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2726  p3 = QPointF( tempPt.x(), tempPt.y() );
2727  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2728  p4 = QPointF( tempPt.x(), tempPt.y() );
2729  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2730  p5 = QPointF( tempPt.x(), tempPt.y() );
2731  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2732  p6 = QPointF( tempPt.x(), tempPt.y() );
2733 
2734  //update p1, p2 last
2735  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2736  p1 = QPointF( tempPt.x(), tempPt.y() );
2737  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2738  p2 = QPointF( tempPt.x(), tempPt.y() );
2739  }
2740 
2741  QPainter p( &patternImage );
2742 
2743 #if 0
2744  // DEBUG: Draw rectangle
2745  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2746  QPen pen( QColor( Qt::black ) );
2747  pen.setWidthF( 0.1 );
2748  pen.setCapStyle( Qt::FlatCap );
2749  p.setPen( pen );
2750 
2751  // To see this rectangle, comment buffer cut below.
2752  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2753  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2754  p.drawPolygon( polygon );
2755 
2756  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 );
2757  p.drawPolygon( polygon );
2758 #endif
2759 
2760  // Use antialiasing because without antialiasing lines are rendered to the
2761  // right and below the mathematically defined points (not symmetrical)
2762  // and such tiles become useless for are filling
2763  p.setRenderHint( QPainter::Antialiasing, true );
2764 
2765  // line rendering needs context for drawing on patternImage
2766  QgsRenderContext lineRenderContext;
2767  lineRenderContext.setPainter( &p );
2768  lineRenderContext.setRasterScaleFactor( 1.0 );
2769  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2771  lineRenderContext.setMapToPixel( mtp );
2772  lineRenderContext.setForceVectorOutput( false );
2773  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2774 
2775  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2776 
2777  QVector<QPolygonF> polygons;
2778  polygons.append( QPolygonF() << p1 << p2 );
2779  polygons.append( QPolygonF() << p3 << p4 );
2780  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2781  {
2782  polygons.append( QPolygonF() << p5 << p6 );
2783  }
2784 
2785  Q_FOREACH ( const QPolygonF& polygon, polygons )
2786  {
2787  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2788  }
2789 
2790  fillLineSymbol->stopRender( lineRenderContext );
2791  p.end();
2792 
2793  // Cut off the buffer
2794  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2795 
2796  //set image to mBrush
2797  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2798  {
2799  QImage transparentImage = patternImage.copy();
2800  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2801  brush.setTextureImage( transparentImage );
2802  }
2803  else
2804  {
2805  brush.setTextureImage( patternImage );
2806  }
2807 
2808  QTransform brushTransform;
2809  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2810  brush.setTransform( brushTransform );
2811 
2812  delete fillLineSymbol;
2813 }
2814 
2816 {
2817  applyPattern( context, mBrush, mLineAngle, mDistance, mLineWidth, mColor );
2818 
2819  if ( mFillLineSymbol )
2820  {
2821  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2822  }
2823 
2824  prepareExpressions( context );
2825 }
2826 
2828 {
2829 }
2830 
2832 {
2833  QgsStringMap map;
2834  map.insert( "angle", QString::number( mLineAngle ) );
2835  map.insert( "distance", QString::number( mDistance ) );
2836  map.insert( "line_width", QString::number( mLineWidth ) );
2838  map.insert( "offset", QString::number( mOffset ) );
2840  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
2842  map.insert( "distance_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2843  map.insert( "line_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2844  map.insert( "offset_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2845  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2846  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2848  return map;
2849 }
2850 
2852 {
2854  if ( mFillLineSymbol )
2855  {
2856  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2857  }
2858  copyPaintEffect( clonedLayer );
2859  return clonedLayer;
2860 }
2861 
2863 {
2864  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2865  if ( !props.value( "uom", "" ).isEmpty() )
2866  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2867  element.appendChild( symbolizerElem );
2868 
2869  // <Geometry>
2870  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2871 
2872  QDomElement fillElem = doc.createElement( "se:Fill" );
2873  symbolizerElem.appendChild( fillElem );
2874 
2875  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2876  fillElem.appendChild( graphicFillElem );
2877 
2878  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2879  graphicFillElem.appendChild( graphicElem );
2880 
2881  //line properties must be inside the graphic definition
2882  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2883  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2884  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
2885 
2886  // <Rotation>
2887  QString angleFunc;
2888  bool ok;
2889  double angle = props.value( "angle", "0" ).toDouble( &ok );
2890  if ( !ok )
2891  {
2892  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
2893  }
2894  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2895  {
2896  angleFunc = QString::number( angle + mLineAngle );
2897  }
2898  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2899 
2900  // <se:Displacement>
2901  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2902  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
2903 }
2904 
2906 {
2907  QString featureStyle;
2908  featureStyle.append( "Brush(" );
2909  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
2910  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
2911  featureStyle.append( ",id:\"ogr-brush-2\"" );
2912  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
2913  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2914  featureStyle.append( ",dx:0mm" );
2915  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2916  featureStyle.append( ')' );
2917  return featureStyle;
2918 }
2919 
2921 {
2924  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2925  {
2926  return; //no data defined settings
2927  }
2928 
2929  bool ok;
2930  double lineAngle = mLineAngle;
2932  {
2935  }
2936  double distance = mDistance;
2938  {
2941  }
2942  double lineWidth = mLineWidth;
2944  {
2947  }
2948  QColor color = mColor;
2950  {
2953  if ( ok )
2954  color = QgsSymbolLayerV2Utils::decodeColor( colorString );
2955  }
2956  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
2957 }
2958 
2960 {
2961  QgsDebugMsg( "Entered." );
2962 
2963  QString name;
2964  QColor fillColor, lineColor;
2965  double size, lineWidth;
2966  Qt::PenStyle lineStyle;
2967 
2968  QDomElement fillElem = element.firstChildElement( "Fill" );
2969  if ( fillElem.isNull() )
2970  return nullptr;
2971 
2972  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2973  if ( graphicFillElem.isNull() )
2974  return nullptr;
2975 
2976  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2977  if ( graphicElem.isNull() )
2978  return nullptr;
2979 
2980  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2981  return nullptr;
2982 
2983  if ( name != "horline" )
2984  return nullptr;
2985 
2986  double angle = 0.0;
2987  QString angleFunc;
2988  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2989  {
2990  bool ok;
2991  double d = angleFunc.toDouble( &ok );
2992  if ( ok )
2993  angle = d;
2994  }
2995 
2996  double offset = 0.0;
2997  QPointF vectOffset;
2998  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
2999  {
3000  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
3001  }
3002 
3004  sl->setColor( lineColor );
3005  sl->setLineWidth( lineWidth );
3006  sl->setLineAngle( angle );
3007  sl->setOffset( offset );
3008  sl->setDistance( size );
3009 
3010  // try to get the outline
3011  QDomElement strokeElem = element.firstChildElement( "Stroke" );
3012  if ( !strokeElem.isNull() )
3013  {
3015  if ( l )
3016  {
3017  QgsSymbolLayerV2List layers;
3018  layers.append( l );
3019  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
3020  }
3021  }
3022 
3023  return sl;
3024 }
3025 
3026 
3028 
3030  mDistanceXUnit( QgsSymbolV2::MM ), mDistanceY( 15 ), mDistanceYUnit( QgsSymbolV2::MM ), mDisplacementX( 0 ), mDisplacementXUnit( QgsSymbolV2::MM ),
3031  mDisplacementY( 0 ), mDisplacementYUnit( QgsSymbolV2::MM )
3032 {
3033  mDistanceX = 15;
3034  mDistanceY = 15;
3035  mDisplacementX = 0;
3036  mDisplacementY = 0;
3038  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no outline
3039 }
3040 
3042 {
3043  delete mMarkerSymbol;
3044 }
3045 
3047 {
3049  mDistanceXUnit = unit;
3050  mDistanceYUnit = unit;
3051  mDisplacementXUnit = unit;
3052  mDisplacementYUnit = unit;
3053 }
3054 
3056 {
3058  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3059  {
3060  return QgsSymbolV2::Mixed;
3061  }
3062  return unit;
3063 }
3064 
3066 {
3068  mDistanceXMapUnitScale = scale;
3069  mDistanceYMapUnitScale = scale;
3072 }
3073 
3075 {
3080  {
3081  return mDistanceXMapUnitScale;
3082  }
3083  return QgsMapUnitScale();
3084 }
3085 
3087 {
3089  if ( properties.contains( "distance_x" ) )
3090  {
3091  layer->setDistanceX( properties["distance_x"].toDouble() );
3092  }
3093  if ( properties.contains( "distance_y" ) )
3094  {
3095  layer->setDistanceY( properties["distance_y"].toDouble() );
3096  }
3097  if ( properties.contains( "displacement_x" ) )
3098  {
3099  layer->setDisplacementX( properties["displacement_x"].toDouble() );
3100  }
3101  if ( properties.contains( "displacement_y" ) )
3102  {
3103  layer->setDisplacementY( properties["displacement_y"].toDouble() );
3104  }
3105 
3106  if ( properties.contains( "distance_x_unit" ) )
3107  {
3108  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
3109  }
3110  if ( properties.contains( "distance_x_map_unit_scale" ) )
3111  {
3112  layer->setDistanceXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_x_map_unit_scale"] ) );
3113  }
3114  if ( properties.contains( "distance_y_unit" ) )
3115  {
3116  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
3117  }
3118  if ( properties.contains( "distance_y_map_unit_scale" ) )
3119  {
3120  layer->setDistanceYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_y_map_unit_scale"] ) );
3121  }
3122  if ( properties.contains( "displacement_x_unit" ) )
3123  {
3124  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
3125  }
3126  if ( properties.contains( "displacement_x_map_unit_scale" ) )
3127  {
3128  layer->setDisplacementXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_x_map_unit_scale"] ) );
3129  }
3130  if ( properties.contains( "displacement_y_unit" ) )
3131  {
3132  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
3133  }
3134  if ( properties.contains( "displacement_y_map_unit_scale" ) )
3135  {
3136  layer->setDisplacementYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_y_map_unit_scale"] ) );
3137  }
3138  if ( properties.contains( "outline_width_unit" ) )
3139  {
3140  layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
3141  }
3142  if ( properties.contains( "outline_width_map_unit_scale" ) )
3143  {
3144  layer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
3145  }
3146 
3147  layer->restoreDataDefinedProperties( properties );
3148 
3149  return layer;
3150 }
3151 
3153 {
3154  return "PointPatternFill";
3155 }
3156 
3157 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
3158  double displacementX, double displacementY )
3159 {
3160  //render 3 rows and columns in one go to easily incorporate displacement
3161  const QgsRenderContext& ctx = context.renderContext();
3164 
3165  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3166  {
3167  QImage img;
3168  brush.setTextureImage( img );
3169  return;
3170  }
3171 
3172  QImage patternImage( width, height, QImage::Format_ARGB32 );
3173  patternImage.fill( 0 );
3174 
3175  if ( mMarkerSymbol )
3176  {
3177  QPainter p( &patternImage );
3178 
3179  //marker rendering needs context for drawing on patternImage
3180  QgsRenderContext pointRenderContext;
3181  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3182  pointRenderContext.setPainter( &p );
3183  pointRenderContext.setRasterScaleFactor( 1.0 );
3184  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
3186  pointRenderContext.setMapToPixel( mtp );
3187  pointRenderContext.setForceVectorOutput( false );
3188  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3189 
3190  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3191 
3192  //render corner points
3193  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3194  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3195  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3196  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3197 
3198  //render displaced points
3200  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit, mDisplacementYMapUnitScale );
3201  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3202  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3203  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3204  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3205  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3206 
3207  mMarkerSymbol->stopRender( pointRenderContext );
3208  }
3209 
3210  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
3211  {
3212  QImage transparentImage = patternImage.copy();
3213  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
3214  brush.setTextureImage( transparentImage );
3215  }
3216  else
3217  {
3218  brush.setTextureImage( patternImage );
3219  }
3220  QTransform brushTransform;
3221  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
3222  brush.setTransform( brushTransform );
3223 }
3224 
3226 {
3227  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3228 
3229  if ( mOutline )
3230  {
3231  mOutline->startRender( context.renderContext(), context.fields() );
3232  }
3233  prepareExpressions( context );
3234 }
3235 
3237 {
3238  if ( mOutline )
3239  {
3240  mOutline->stopRender( context.renderContext() );
3241  }
3242 }
3243 
3245 {
3246  QgsStringMap map;
3247  map.insert( "distance_x", QString::number( mDistanceX ) );
3248  map.insert( "distance_y", QString::number( mDistanceY ) );
3249  map.insert( "displacement_x", QString::number( mDisplacementX ) );
3250  map.insert( "displacement_y", QString::number( mDisplacementY ) );
3251  map.insert( "distance_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit ) );
3252  map.insert( "distance_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit ) );
3253  map.insert( "displacement_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit ) );
3254  map.insert( "displacement_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit ) );
3255  map.insert( "distance_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3256  map.insert( "distance_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3257  map.insert( "displacement_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3258  map.insert( "displacement_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3259  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
3260  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
3262  return map;
3263 }
3264 
3266 {
3268  if ( mMarkerSymbol )
3269  {
3270  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3271  }
3272  copyPaintEffect( clonedLayer );
3273  return clonedLayer;
3274 }
3275 
3277 {
3278  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3279  {
3280  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
3281  if ( !props.value( "uom", "" ).isEmpty() )
3282  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
3283  element.appendChild( symbolizerElem );
3284 
3285  // <Geometry>
3286  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
3287 
3288  QDomElement fillElem = doc.createElement( "se:Fill" );
3289  symbolizerElem.appendChild( fillElem );
3290 
3291  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
3292  fillElem.appendChild( graphicFillElem );
3293 
3294  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3296  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
3297  symbolizerElem.appendChild( distanceElem );
3298 
3300  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
3301  if ( !markerLayer )
3302  {
3303  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3304  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3305  }
3306  else
3307  {
3308  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3309  }
3310  }
3311 }
3312 
3314 {
3315  Q_UNUSED( element );
3316  return nullptr;
3317 }
3318 
3320 {
3321  if ( !symbol )
3322  {
3323  return false;
3324  }
3325 
3326  if ( symbol->type() == QgsSymbolV2::Marker )
3327  {
3328  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
3329  delete mMarkerSymbol;
3330  mMarkerSymbol = markerSymbol;
3331  }
3332  return true;
3333 }
3334 
3336 {
3340  {
3341  return;
3342  }
3343 
3344  double distanceX = mDistanceX;
3346  {
3349  }
3350  double distanceY = mDistanceY;
3352  {
3355  }
3356  double displacementX = mDisplacementX;
3358  {
3361  }
3362  double displacementY = mDisplacementY;
3364  {
3367  }
3368  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3369 }
3370 
3372 {
3373  return 0;
3374 }
3375 
3377 {
3379 
3380  if ( mMarkerSymbol )
3381  attributes.unite( mMarkerSymbol->usedAttributes() );
3382 
3383  return attributes;
3384 }
3385 
3387 
3388 
3389 QgsCentroidFillSymbolLayerV2::QgsCentroidFillSymbolLayerV2(): mMarker( nullptr ), mPointOnSurface( false ), mPointOnAllParts( true )
3390 {
3392 }
3393 
3395 {
3396  delete mMarker;
3397 }
3398 
3400 {
3402 
3403  if ( properties.contains( "point_on_surface" ) )
3404  sl->setPointOnSurface( properties["point_on_surface"].toInt() != 0 );
3405  if ( properties.contains( "point_on_all_parts" ) )
3406  sl->setPointOnAllParts( properties["point_on_all_parts"].toInt() != 0 );
3407 
3408  return sl;
3409 }
3410 
3412 {
3413  return "CentroidFill";
3414 }
3415 
3417 {
3418  mMarker->setColor( color );
3419  mColor = color;
3420 }
3421 
3423 {
3424  mMarker->setAlpha( context.alpha() );
3425  mMarker->startRender( context.renderContext(), context.fields() );
3426 
3427  mCurrentFeatureId = -1;
3428  mBiggestPartIndex = 0;
3429 }
3430 
3432 {
3433  mMarker->stopRender( context.renderContext() );
3434 }
3435 
3437 {
3438  Q_UNUSED( rings );
3439 
3440  if ( !mPointOnAllParts )
3441  {
3442  const QgsFeature* feature = context.feature();
3443  if ( feature )
3444  {
3445  if ( feature->id() != mCurrentFeatureId )
3446  {
3447  mCurrentFeatureId = feature->id();
3448  mBiggestPartIndex = 1;
3449 
3450  if ( context.geometryPartCount() > 1 )
3451  {
3452  const QgsGeometry *geom = feature->constGeometry();
3453  const QgsGeometryCollectionV2* geomCollection = dynamic_cast<const QgsGeometryCollectionV2*>( geom->geometry() );
3454 
3455  double area = 0;
3456  double areaBiggest = 0;
3457  for ( int i = 0; i < context.geometryPartCount(); ++i )
3458  {
3459  area = geomCollection->geometryN( i )->area();
3460  if ( area > areaBiggest )
3461  {
3462  areaBiggest = area;
3463  mBiggestPartIndex = i + 1;
3464  }
3465  }
3466  }
3467  }
3468  }
3469  }
3470 
3471  QgsDebugMsg( QString( "num: %1, count: %2" ).arg( context.geometryPartNum() ).arg( context.geometryPartCount() ) );
3472 
3473  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3474  {
3476  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3477  }
3478 }
3479 
3481 {
3482  QgsStringMap map;
3483  map["point_on_surface"] = QString::number( mPointOnSurface );
3484  map["point_on_all_parts"] = QString::number( mPointOnAllParts );
3485  return map;
3486 }
3487 
3489 {
3491  x->mAngle = mAngle;
3492  x->mColor = mColor;
3493  x->setSubSymbol( mMarker->clone() );
3496  copyPaintEffect( x );
3497  return x;
3498 }
3499 
3501 {
3502  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3503  // used with PointSymbolizer, then the semantic is to use the centroid
3504  // of the geometry, or any similar representative point.
3505  mMarker->toSld( doc, element, props );
3506 }
3507 
3509 {
3510  QgsDebugMsg( "Entered." );
3511 
3513  if ( !l )
3514  return nullptr;
3515 
3516  QgsSymbolLayerV2List layers;
3517  layers.append( l );
3518  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
3519 
3521  sl->setSubSymbol( marker );
3522  return sl;
3523 }
3524 
3525 
3527 {
3528  return mMarker;
3529 }
3530 
3532 {
3533  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
3534  {
3535  delete symbol;
3536  return false;
3537  }
3538 
3539  delete mMarker;
3540  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
3541  mColor = mMarker->color();
3542  return true;
3543 }
3544 
3546 {
3548 
3549  if ( mMarker )
3550  attributes.unite( mMarker->usedAttributes() );
3551 
3552  return attributes;
3553 }
3554 
3556 {
3557  if ( mMarker )
3558  {
3559  mMarker->setOutputUnit( unit );
3560  }
3561 }
3562 
3564 {
3565  if ( mMarker )
3566  {
3567  return mMarker->outputUnit();
3568  }
3569  return QgsSymbolV2::Mixed; //mOutputUnit;
3570 }
3571 
3573 {
3574  if ( mMarker )
3575  {
3576  mMarker->setMapUnitScale( scale );
3577  }
3578 }
3579 
3581 {
3582  if ( mMarker )
3583  {
3584  return mMarker->mapUnitScale();
3585  }
3586  return QgsMapUnitScale();
3587 }
3588 
3589 
3590 
3591 
3594  , mImageFilePath( imageFilePath )
3595  , mCoordinateMode( QgsRasterFillSymbolLayer::Feature )
3596  , mAlpha( 1.0 )
3597  , mOffsetUnit( QgsSymbolV2::MM )
3598  , mWidth( 0.0 )
3599  , mWidthUnit( QgsSymbolV2::Pixel )
3600 {
3601  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3602 }
3603 
3605 {
3606 
3607 }
3608 
3610 {
3612  double alpha = 1.0;
3613  QPointF offset;
3614  double angle = 0.0;
3615  double width = 0.0;
3616 
3617  QString imagePath;
3618  if ( properties.contains( "imageFile" ) )
3619  {
3620  imagePath = properties["imageFile"];
3621  }
3622  if ( properties.contains( "coordinate_mode" ) )
3623  {
3624  mode = static_cast< FillCoordinateMode >( properties["coordinate_mode"].toInt() );
3625  }
3626  if ( properties.contains( "alpha" ) )
3627  {
3628  alpha = properties["alpha"].toDouble();
3629  }
3630  if ( properties.contains( "offset" ) )
3631  {
3632  offset = QgsSymbolLayerV2Utils::decodePoint( properties["offset"] );
3633  }
3634  if ( properties.contains( "angle" ) )
3635  {
3636  angle = properties["angle"].toDouble();
3637  }
3638  if ( properties.contains( "width" ) )
3639  {
3640  width = properties["width"].toDouble();
3641  }
3642  QgsRasterFillSymbolLayer* symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
3643  symbolLayer->setCoordinateMode( mode );
3644  symbolLayer->setAlpha( alpha );
3645  symbolLayer->setOffset( offset );
3646  symbolLayer->setAngle( angle );
3647  symbolLayer->setWidth( width );
3648  if ( properties.contains( "offset_unit" ) )
3649  {
3650  symbolLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
3651  }
3652  if ( properties.contains( "offset_map_unit_scale" ) )
3653  {
3654  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
3655  }
3656  if ( properties.contains( "width_unit" ) )
3657  {
3658  symbolLayer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["width_unit"] ) );
3659  }
3660  if ( properties.contains( "width_map_unit_scale" ) )
3661  {
3662  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["width_map_unit_scale"] ) );
3663  }
3664 
3665  symbolLayer->restoreDataDefinedProperties( properties );
3666 
3667  return symbolLayer;
3668 }
3669 
3671 {
3672  Q_UNUSED( symbol );
3673  return true;
3674 }
3675 
3677 {
3678  return "RasterFill";
3679 }
3680 
3682 {
3683  QPainter* p = context.renderContext().painter();
3684  if ( !p )
3685  {
3686  return;
3687  }
3688 
3689  QPointF offset;
3690  if ( !mOffset.isNull() )
3691  {
3694  p->translate( offset );
3695  }
3696  if ( mCoordinateMode == Feature )
3697  {
3698  QRectF boundingRect = points.boundingRect();
3699  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3700  boundingRect.top() - mBrush.transform().dy() ) );
3701  }
3702 
3703  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3704  if ( !mOffset.isNull() )
3705  {
3706  p->translate( -offset );
3707  }
3708 }
3709 
3711 {
3712  prepareExpressions( context );
3713  applyPattern( mBrush, mImageFilePath, mWidth, mAlpha, context );
3714 }
3715 
3717 {
3718  Q_UNUSED( context );
3719 }
3720 
3722 {
3723  QgsStringMap map;
3724  map["imageFile"] = mImageFilePath;
3725  map["coordinate_mode"] = QString::number( mCoordinateMode );
3726  map["alpha"] = QString::number( mAlpha );
3727  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
3728  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
3729  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
3730  map["angle"] = QString::number( mAngle );
3731  map["width"] = QString::number( mWidth );
3732  map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
3733  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
3734 
3736  return map;
3737 }
3738 
3740 {
3743  sl->setAlpha( mAlpha );
3744  sl->setOffset( mOffset );
3745  sl->setOffsetUnit( mOffsetUnit );
3747  sl->setAngle( mAngle );
3748  sl->setWidth( mWidth );
3749  sl->setWidthUnit( mWidthUnit );
3752  copyPaintEffect( sl );
3753  return sl;
3754 }
3755 
3757 {
3758  return mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
3759 }
3760 
3762 {
3763  mImageFilePath = imagePath;
3764 }
3765 
3767 {
3768  mCoordinateMode = mode;
3769 }
3770 
3772 {
3773  mAlpha = alpha;
3774 }
3775 
3777 {
3778  if ( !hasDataDefinedProperties() )
3779  return; // shortcut
3780 
3781  bool hasWidthExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH );
3782  bool hasFileExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILE );
3783  bool hasAlphaExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ALPHA );
3784  bool hasAngleExpression = hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE );
3785 
3786  if ( !hasWidthExpression && !hasAngleExpression && !hasAlphaExpression && !hasFileExpression )
3787  {
3788  return; //no data defined settings
3789  }
3790 
3791  bool ok;
3792  if ( hasAngleExpression )
3793  {
3794  context.setOriginalValueVariable( mAngle );
3795  double nextAngle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, QVariant(), &ok ).toDouble();
3796  if ( ok )
3797  mNextAngle = nextAngle;
3798  }
3799 
3800  if ( !hasWidthExpression && !hasAlphaExpression && !hasFileExpression )
3801  {
3802  return; //nothing further to do
3803  }
3804 
3805  double width = mWidth;
3806  if ( hasWidthExpression )
3807  {
3808  context.setOriginalValueVariable( mWidth );
3810  }
3811  double alpha = mAlpha;
3812  if ( hasAlphaExpression )
3813  {
3814  context.setOriginalValueVariable( mAlpha );
3816  }
3817  QString file = mImageFilePath;
3818  if ( hasFileExpression )
3819  {
3822  }
3823  applyPattern( mBrush, file, width, alpha, context );
3824 }
3825 
3826 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolV2RenderContext &context )
3827 {
3828  QImage image( imageFilePath );
3829  if ( image.isNull() )
3830  {
3831  return;
3832  }
3833  if ( !image.hasAlphaChannel() )
3834  {
3835  image = image.convertToFormat( QImage::Format_ARGB32 );
3836  }
3837 
3838  double pixelWidth;
3839  if ( width > 0 )
3840  {
3842  }
3843  else
3844  {
3845  pixelWidth = image.width();
3846  }
3847 
3848  //reduce alpha of image
3849  if ( alpha < 1.0 )
3850  {
3851  QPainter p;
3852  p.begin( &image );
3853  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3854  QColor alphaColor( 0, 0, 0 );
3855  alphaColor.setAlphaF( alpha );
3856  p.fillRect( image.rect(), alphaColor );
3857  p.end();
3858  }
3859 
3860  //resize image if required
3861  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3862  {
3863  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3864  }
3865 
3866  brush.setTextureImage( image );
3867 }
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:62
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
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:104
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
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:93
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:79
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:348
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:78
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:362
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:66
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:64
#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:374
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 image data.
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
static const QString EXPR_ALPHA
QgsSymbolV2::OutputUnit outputUnit() const override
double dxfWidth(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
get line width
QgsVectorColorRampV2 * mTwoColorGradientRamp
void setPen(const QColor &color)
void setLineWidthUnit(QgsSymbolV2::OutputUnit unit)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
int width() const
QgsSymbolV2::OutputUnit outputUnit() const override
void setAttribute(const QString &name, const QString &value)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
virtual QgsVectorColorRampV2 * clone() const =0
Creates a clone of the color ramp.
virtual QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
bool useWholeShape() const
Returns whether the shapeburst fill is set to cover the entire shape.