QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsfillsymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfillsymbollayer.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 "qgsfillsymbollayer.h"
17 #include "qgslinesymbollayer.h"
18 #include "qgsmarkersymbollayer.h"
19 #include "qgssymbollayerutils.h"
20 #include "qgsdxfexport.h"
21 #include "qgsexpression.h"
22 #include "qgsgeometry.h"
23 #include "qgsgeometrycollection.h"
24 #include "qgsrendercontext.h"
25 #include "qgsproject.h"
26 #include "qgssvgcache.h"
27 #include "qgslogger.h"
28 #include "qgscolorramp.h"
29 #include "qgsunittypes.h"
30 #include "qgsmessagelog.h"
31 
32 #include <QPainter>
33 #include <QFile>
34 #include <QSvgRenderer>
35 #include <QDomDocument>
36 #include <QDomElement>
37 
38 QgsSimpleFillSymbolLayer::QgsSimpleFillSymbolLayer( const QColor &color, Qt::BrushStyle style, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth,
39  Qt::PenJoinStyle penJoinStyle )
40  : mBrushStyle( style )
41  , mStrokeColor( strokeColor )
42  , mStrokeStyle( strokeStyle )
43  , mStrokeWidth( strokeWidth )
44  , mPenJoinStyle( penJoinStyle )
45 {
46  mColor = color;
47 }
48 
50 {
51  mStrokeWidthUnit = unit;
52  mOffsetUnit = unit;
53 }
54 
56 {
58  if ( mOffsetUnit != unit )
59  {
61  }
62  return unit;
63 }
64 
66 {
68  mOffsetMapUnitScale = scale;
69 }
70 
72 {
74  {
76  }
77  return QgsMapUnitScale();
78 }
79 
80 void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
81 {
82  if ( !dataDefinedProperties().hasActiveProperties() )
83  return; // shortcut
84 
85  bool ok;
86 
88  {
91  }
93  {
96  if ( exprVal.isValid() )
97  brush.setStyle( QgsSymbolLayerUtils::decodeBrushStyle( exprVal.toString() ) );
98  }
100  {
103  }
105  {
108  double width = exprVal.toDouble( &ok );
109  if ( ok )
110  {
112  pen.setWidthF( width );
113  selPen.setWidthF( width );
114  }
115  }
117  {
120  if ( ok )
121  {
122  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
123  selPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
124  }
125  }
127  {
130  if ( ok )
131  {
132  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
133  selPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
134  }
135  }
136 }
137 
138 
140 {
142  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
146  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
147  QPointF offset;
148 
149  if ( props.contains( QStringLiteral( "color" ) ) )
150  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
151  if ( props.contains( QStringLiteral( "style" ) ) )
152  style = QgsSymbolLayerUtils::decodeBrushStyle( props[QStringLiteral( "style" )] );
153  if ( props.contains( QStringLiteral( "color_border" ) ) )
154  {
155  //pre 2.5 projects used "color_border"
156  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
157  }
158  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
159  {
160  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
161  }
162  else if ( props.contains( QStringLiteral( "line_color" ) ) )
163  {
164  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
165  }
166 
167  if ( props.contains( QStringLiteral( "style_border" ) ) )
168  {
169  //pre 2.5 projects used "style_border"
170  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "style_border" )] );
171  }
172  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
173  {
174  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] );
175  }
176  else if ( props.contains( QStringLiteral( "line_style" ) ) )
177  {
178  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] );
179  }
180  if ( props.contains( QStringLiteral( "width_border" ) ) )
181  {
182  //pre 2.5 projects used "width_border"
183  strokeWidth = props[QStringLiteral( "width_border" )].toDouble();
184  }
185  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
186  {
187  strokeWidth = props[QStringLiteral( "outline_width" )].toDouble();
188  }
189  else if ( props.contains( QStringLiteral( "line_width" ) ) )
190  {
191  strokeWidth = props[QStringLiteral( "line_width" )].toDouble();
192  }
193  if ( props.contains( QStringLiteral( "offset" ) ) )
194  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
195  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
196  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
197 
198  QgsSimpleFillSymbolLayer *sl = new QgsSimpleFillSymbolLayer( color, style, strokeColor, strokeStyle, strokeWidth, penJoinStyle );
199  sl->setOffset( offset );
200  if ( props.contains( QStringLiteral( "border_width_unit" ) ) )
201  {
202  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "border_width_unit" )] ) );
203  }
204  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
205  {
206  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
207  }
208  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
209  {
210  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
211  }
212  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
213  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
214 
215  if ( props.contains( QStringLiteral( "border_width_map_unit_scale" ) ) )
216  sl->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "border_width_map_unit_scale" )] ) );
217  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
218  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
219 
220  sl->restoreOldDataDefinedProperties( props );
221 
222  return sl;
223 }
224 
225 
227 {
228  return QStringLiteral( "SimpleFill" );
229 }
230 
232 {
233  QColor fillColor = mColor;
234  fillColor.setAlphaF( context.opacity() * mColor.alphaF() );
235  mBrush = QBrush( fillColor, mBrushStyle );
236 
237  QColor selColor = context.renderContext().selectionColor();
238  QColor selPenColor = selColor == mColor ? selColor : mStrokeColor;
239  if ( ! SELECTION_IS_OPAQUE ) selColor.setAlphaF( context.opacity() );
240  mSelBrush = QBrush( selColor );
241  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
242  // this would mean symbols with "no fill" look the same whether or not they are selected
243  if ( SELECT_FILL_STYLE )
244  mSelBrush.setStyle( mBrushStyle );
245 
246  QColor strokeColor = mStrokeColor;
247  strokeColor.setAlphaF( context.opacity() * mStrokeColor.alphaF() );
248  mPen = QPen( strokeColor );
249  mSelPen = QPen( selPenColor );
250  mPen.setStyle( mStrokeStyle );
252  mPen.setJoinStyle( mPenJoinStyle );
253 }
254 
256 {
257  Q_UNUSED( context );
258 }
259 
260 void QgsSimpleFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
261 {
262  QPainter *p = context.renderContext().painter();
263  if ( !p )
264  {
265  return;
266  }
267 
268  applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );
269 
270  p->setBrush( context.selected() ? mSelBrush : mBrush );
271  p->setPen( context.selected() ? mSelPen : mPen );
272 
273  QPointF offset;
274  if ( !mOffset.isNull() )
275  {
278  p->translate( offset );
279  }
280 
281  _renderPolygon( p, points, rings, context );
282 
283  if ( !mOffset.isNull() )
284  {
285  p->translate( -offset );
286  }
287 }
288 
290 {
291  QgsStringMap map;
292  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
293  map[QStringLiteral( "style" )] = QgsSymbolLayerUtils::encodeBrushStyle( mBrushStyle );
294  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
295  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
296  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
297  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
298  map[QStringLiteral( "border_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
299  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
300  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
301  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
302  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
303  return map;
304 }
305 
307 {
309  sl->setOffset( mOffset );
310  sl->setOffsetUnit( mOffsetUnit );
315  copyPaintEffect( sl );
316  return sl;
317 }
318 
319 void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
320 {
321  if ( mBrushStyle == Qt::NoBrush && mStrokeStyle == Qt::NoPen )
322  return;
323 
324  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
325  if ( !props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ).isEmpty() )
326  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ) );
327  element.appendChild( symbolizerElem );
328 
329  // <Geometry>
330  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QLatin1String( "" ) ) );
331 
332  if ( mBrushStyle != Qt::NoBrush )
333  {
334  // <Fill>
335  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
336  symbolizerElem.appendChild( fillElem );
338  }
339 
340  if ( mStrokeStyle != Qt::NoPen )
341  {
342  // <Stroke>
343  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
344  symbolizerElem.appendChild( strokeElem );
346  QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mStrokeStyle, mStrokeColor, strokeWidth, &mPenJoinStyle );
347  }
348 
349  // <se:Displacement>
351  QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, offset );
352 }
353 
354 QString QgsSimpleFillSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
355 {
356  //brush
357  QString symbolStyle;
358  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStyleBrush( mColor ) );
359  symbolStyle.append( ';' );
360  //pen
361  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStylePen( mStrokeWidth, mmScaleFactor, mapUnitScaleFactor, mStrokeColor, mPenJoinStyle ) );
362  return symbolStyle;
363 }
364 
366 {
367  QgsDebugMsg( "Entered." );
368 
369  QColor color, strokeColor;
370  Qt::BrushStyle fillStyle;
371  Qt::PenStyle strokeStyle;
372  double strokeWidth;
373 
374  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
375  QgsSymbolLayerUtils::fillFromSld( fillElem, fillStyle, color );
376 
377  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
378  QgsSymbolLayerUtils::lineFromSld( strokeElem, strokeStyle, strokeColor, strokeWidth );
379 
380  QPointF offset;
382 
383  QString uom = element.attribute( QStringLiteral( "uom" ), QString() );
384  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
385  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
386  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
387 
388  QgsSimpleFillSymbolLayer *sl = new QgsSimpleFillSymbolLayer( color, fillStyle, strokeColor, strokeStyle, strokeWidth );
389  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
390  sl->setOffset( offset );
391  return sl;
392 }
393 
395 {
396  double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale );
397  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
398  return penBleed + offsetBleed;
399 }
400 
402 {
403  double width = mStrokeWidth;
405  {
408  }
410 }
411 
413 {
414  QColor c = mStrokeColor;
416  {
419  }
420  return c;
421 }
422 
424 {
425  double angle = mAngle;
427  {
428  context.setOriginalValueVariable( mAngle );
430  }
431  return angle;
432 }
433 
435 {
436  return mStrokeStyle;
437 }
438 
440 {
441  QColor c = mColor;
443  {
445  }
446  return c;
447 }
448 
450 {
451  return mBrushStyle;
452 }
453 
454 //QgsGradientFillSymbolLayer
455 
457  GradientColorType colorType, GradientType gradientType,
458  GradientCoordinateMode coordinateMode, GradientSpread spread )
459  : mGradientColorType( colorType )
460  , mGradientType( gradientType )
461  , mCoordinateMode( coordinateMode )
462  , mGradientSpread( spread )
463  , mReferencePoint1( QPointF( 0.5, 0 ) )
464  , mReferencePoint2( QPointF( 0.5, 1 ) )
465 {
466  mColor = color;
467  mColor2 = color2;
468 }
469 
471 {
472  delete mGradientRamp;
473 }
474 
476 {
477  //default to a two-color, linear gradient with feature mode and pad spreading
482  //default to gradient from the default fill color to white
483  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
484  QPointF referencePoint1 = QPointF( 0.5, 0 );
485  bool refPoint1IsCentroid = false;
486  QPointF referencePoint2 = QPointF( 0.5, 1 );
487  bool refPoint2IsCentroid = false;
488  double angle = 0;
489  QPointF offset;
490 
491  //update gradient properties from props
492  if ( props.contains( QStringLiteral( "type" ) ) )
493  type = static_cast< GradientType >( props[QStringLiteral( "type" )].toInt() );
494  if ( props.contains( QStringLiteral( "coordinate_mode" ) ) )
495  coordinateMode = static_cast< GradientCoordinateMode >( props[QStringLiteral( "coordinate_mode" )].toInt() );
496  if ( props.contains( QStringLiteral( "spread" ) ) )
497  gradientSpread = static_cast< GradientSpread >( props[QStringLiteral( "spread" )].toInt() );
498  if ( props.contains( QStringLiteral( "color_type" ) ) )
499  colorType = static_cast< GradientColorType >( props[QStringLiteral( "color_type" )].toInt() );
500  if ( props.contains( QStringLiteral( "gradient_color" ) ) )
501  {
502  //pre 2.5 projects used "gradient_color"
503  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color" )] );
504  }
505  else if ( props.contains( QStringLiteral( "color" ) ) )
506  {
507  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
508  }
509  if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
510  {
511  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
512  }
513 
514  if ( props.contains( QStringLiteral( "reference_point1" ) ) )
515  referencePoint1 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point1" )] );
516  if ( props.contains( QStringLiteral( "reference_point1_iscentroid" ) ) )
517  refPoint1IsCentroid = props[QStringLiteral( "reference_point1_iscentroid" )].toInt();
518  if ( props.contains( QStringLiteral( "reference_point2" ) ) )
519  referencePoint2 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point2" )] );
520  if ( props.contains( QStringLiteral( "reference_point2_iscentroid" ) ) )
521  refPoint2IsCentroid = props[QStringLiteral( "reference_point2_iscentroid" )].toInt();
522  if ( props.contains( QStringLiteral( "angle" ) ) )
523  angle = props[QStringLiteral( "angle" )].toDouble();
524 
525  if ( props.contains( QStringLiteral( "offset" ) ) )
526  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
527 
528  //attempt to create color ramp from props
529  QgsColorRamp *gradientRamp = nullptr;
530  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
531  {
532  gradientRamp = QgsCptCityColorRamp::create( props );
533  }
534  else
535  {
536  gradientRamp = QgsGradientColorRamp::create( props );
537  }
538 
539  //create a new gradient fill layer with desired properties
540  QgsGradientFillSymbolLayer *sl = new QgsGradientFillSymbolLayer( color, color2, colorType, type, coordinateMode, gradientSpread );
541  sl->setOffset( offset );
542  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
543  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
544  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
545  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
546  sl->setReferencePoint1( referencePoint1 );
547  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
548  sl->setReferencePoint2( referencePoint2 );
549  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
550  sl->setAngle( angle );
551  if ( gradientRamp )
552  sl->setColorRamp( gradientRamp );
553 
554  sl->restoreOldDataDefinedProperties( props );
555 
556  return sl;
557 }
558 
560 {
561  delete mGradientRamp;
562  mGradientRamp = ramp;
563 }
564 
566 {
567  return QStringLiteral( "GradientFill" );
568 }
569 
570 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, const QPolygonF &points )
571 {
572  if ( !dataDefinedProperties().hasActiveProperties() && !mReferencePoint1IsCentroid && !mReferencePoint2IsCentroid )
573  {
574  //shortcut
577  return;
578  }
579 
580  bool ok;
581 
582  //first gradient color
583  QColor color = mColor;
585  {
588  }
589 
590  //second gradient color
591  QColor color2 = mColor2;
593  {
596  }
597 
598  //gradient rotation angle
599  double angle = mAngle;
601  {
602  context.setOriginalValueVariable( mAngle );
604  }
605 
606  //gradient type
609  {
611  if ( ok )
612  {
613  if ( currentType == QObject::tr( "linear" ) )
614  {
615  gradientType = QgsGradientFillSymbolLayer::Linear;
616  }
617  else if ( currentType == QObject::tr( "radial" ) )
618  {
619  gradientType = QgsGradientFillSymbolLayer::Radial;
620  }
621  else if ( currentType == QObject::tr( "conical" ) )
622  {
624  }
625  }
626  }
627 
628  //coordinate mode
631  {
632  QString currentCoordMode = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCoordinateMode, context.renderContext().expressionContext(), QString(), &ok );
633  if ( ok )
634  {
635  if ( currentCoordMode == QObject::tr( "feature" ) )
636  {
637  coordinateMode = QgsGradientFillSymbolLayer::Feature;
638  }
639  else if ( currentCoordMode == QObject::tr( "viewport" ) )
640  {
641  coordinateMode = QgsGradientFillSymbolLayer::Viewport;
642  }
643  }
644  }
645 
646  //gradient spread
649  {
651  if ( ok )
652  {
653  if ( currentSpread == QObject::tr( "pad" ) )
654  {
656  }
657  else if ( currentSpread == QObject::tr( "repeat" ) )
658  {
660  }
661  else if ( currentSpread == QObject::tr( "reflect" ) )
662  {
664  }
665  }
666  }
667 
668  //reference point 1 x & y
669  double refPoint1X = mReferencePoint1.x();
671  {
672  context.setOriginalValueVariable( refPoint1X );
674  }
675  double refPoint1Y = mReferencePoint1.y();
677  {
678  context.setOriginalValueVariable( refPoint1Y );
680  }
681  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
683  {
684  context.setOriginalValueVariable( refPoint1IsCentroid );
686  }
687 
688  //reference point 2 x & y
689  double refPoint2X = mReferencePoint2.x();
691  {
692  context.setOriginalValueVariable( refPoint2X );
694  }
695  double refPoint2Y = mReferencePoint2.y();
697  {
698  context.setOriginalValueVariable( refPoint2Y );
700  }
701  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
703  {
704  context.setOriginalValueVariable( refPoint2IsCentroid );
706  }
707 
708  if ( refPoint1IsCentroid || refPoint2IsCentroid )
709  {
710  //either the gradient is starting or ending at a centroid, so calculate it
711  QPointF centroid = QgsSymbolLayerUtils::polygonCentroid( points );
712  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
713  QRectF bbox = points.boundingRect();
714  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
715  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
716 
717  if ( refPoint1IsCentroid )
718  {
719  refPoint1X = centroidX;
720  refPoint1Y = centroidY;
721  }
722  if ( refPoint2IsCentroid )
723  {
724  refPoint2X = centroidX;
725  refPoint2Y = centroidY;
726  }
727  }
728 
729  //update gradient with data defined values
730  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
731  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
732 }
733 
734 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint, double angle )
735 {
736  //rotate a reference point by a specified angle around the point (0.5, 0.5)
737 
738  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
739  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
740  //rotate this line by the current rotation angle
741  refLine.setAngle( refLine.angle() + angle );
742  //get new end point of line
743  QPointF rotatedReferencePoint = refLine.p2();
744  //make sure coords of new end point is within [0, 1]
745  if ( rotatedReferencePoint.x() > 1 )
746  rotatedReferencePoint.setX( 1 );
747  if ( rotatedReferencePoint.x() < 0 )
748  rotatedReferencePoint.setX( 0 );
749  if ( rotatedReferencePoint.y() > 1 )
750  rotatedReferencePoint.setY( 1 );
751  if ( rotatedReferencePoint.y() < 0 )
752  rotatedReferencePoint.setY( 0 );
753 
754  return rotatedReferencePoint;
755 }
756 
757 void QgsGradientFillSymbolLayer::applyGradient( const QgsSymbolRenderContext &context, QBrush &brush,
758  const QColor &color, const QColor &color2, GradientColorType gradientColorType,
759  QgsColorRamp *gradientRamp, GradientType gradientType,
761  QPointF referencePoint1, QPointF referencePoint2, const double angle )
762 {
763  //update alpha of gradient colors
764  QColor fillColor = color;
765  fillColor.setAlphaF( context.opacity() * fillColor.alphaF() );
766  QColor fillColor2 = color2;
767  fillColor2.setAlphaF( context.opacity() * fillColor2.alphaF() );
768 
769  //rotate reference points
770  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
771  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
772 
773  //create a QGradient with the desired properties
774  QGradient gradient;
775  switch ( gradientType )
776  {
778  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
779  break;
781  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
782  break;
784  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
785  break;
786  }
787  switch ( coordinateMode )
788  {
790  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
791  break;
793  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
794  break;
795  }
796  switch ( gradientSpread )
797  {
799  gradient.setSpread( QGradient::PadSpread );
800  break;
802  gradient.setSpread( QGradient::ReflectSpread );
803  break;
805  gradient.setSpread( QGradient::RepeatSpread );
806  break;
807  }
808 
809  //add stops to gradient
811  ( gradientRamp->type() == QLatin1String( "gradient" ) || gradientRamp->type() == QLatin1String( "cpt-city" ) ) )
812  {
813  //color ramp gradient
814  QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( gradientRamp );
815  gradRamp->addStopsToGradient( &gradient, context.opacity() );
816  }
817  else
818  {
819  //two color gradient
820  gradient.setColorAt( 0.0, fillColor );
821  gradient.setColorAt( 1.0, fillColor2 );
822  }
823 
824  //update QBrush use gradient
825  brush = QBrush( gradient );
826 }
827 
829 {
830  QColor selColor = context.renderContext().selectionColor();
831  if ( ! SELECTION_IS_OPAQUE )
832  selColor.setAlphaF( context.opacity() );
833  mSelBrush = QBrush( selColor );
834 }
835 
837 {
838  Q_UNUSED( context );
839 }
840 
841 void QgsGradientFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
842 {
843  QPainter *p = context.renderContext().painter();
844  if ( !p )
845  {
846  return;
847  }
848 
849  applyDataDefinedSymbology( context, points );
850 
851  p->setBrush( context.selected() ? mSelBrush : mBrush );
852  p->setPen( Qt::NoPen );
853 
854  QPointF offset;
855  if ( !mOffset.isNull() )
856  {
859  p->translate( offset );
860  }
861 
862  _renderPolygon( p, points, rings, context );
863 
864  if ( !mOffset.isNull() )
865  {
866  p->translate( -offset );
867  }
868 }
869 
871 {
872  QgsStringMap map;
873  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
874  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
875  map[QStringLiteral( "color_type" )] = QString::number( mGradientColorType );
876  map[QStringLiteral( "type" )] = QString::number( mGradientType );
877  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
878  map[QStringLiteral( "spread" )] = QString::number( mGradientSpread );
879  map[QStringLiteral( "reference_point1" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint1 );
880  map[QStringLiteral( "reference_point1_iscentroid" )] = QString::number( mReferencePoint1IsCentroid );
881  map[QStringLiteral( "reference_point2" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint2 );
882  map[QStringLiteral( "reference_point2_iscentroid" )] = QString::number( mReferencePoint2IsCentroid );
883  map[QStringLiteral( "angle" )] = QString::number( mAngle );
884  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
885  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
886  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
887  if ( mGradientRamp )
888  {
889  map.unite( mGradientRamp->properties() );
890  }
891  return map;
892 }
893 
895 {
897  if ( mGradientRamp )
898  sl->setColorRamp( mGradientRamp->clone() );
903  sl->setAngle( mAngle );
904  sl->setOffset( mOffset );
905  sl->setOffsetUnit( mOffsetUnit );
908  copyPaintEffect( sl );
909  return sl;
910 }
911 
913 {
914  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
915  return offsetBleed;
916 }
917 
919 {
920  mOffsetUnit = unit;
921 }
922 
924 {
925  return mOffsetUnit;
926 }
927 
929 {
930  mOffsetMapUnitScale = scale;
931 }
932 
934 {
935  return mOffsetMapUnitScale;
936 }
937 
938 //QgsShapeburstFillSymbolLayer
939 
941  int blurRadius, bool useWholeShape, double maxDistance )
942  : mBlurRadius( blurRadius )
943  , mUseWholeShape( useWholeShape )
944  , mMaxDistance( maxDistance )
945  , mColorType( colorType )
946  , mColor2( color2 )
947 {
948  mColor = color;
949 }
950 
952 
954 {
955  //default to a two-color gradient
957  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
958  int blurRadius = 0;
959  bool useWholeShape = true;
960  double maxDistance = 5;
961  QPointF offset;
962 
963  //update fill properties from props
964  if ( props.contains( QStringLiteral( "color_type" ) ) )
965  {
966  colorType = static_cast< ShapeburstColorType >( props[QStringLiteral( "color_type" )].toInt() );
967  }
968  if ( props.contains( QStringLiteral( "shapeburst_color" ) ) )
969  {
970  //pre 2.5 projects used "shapeburst_color"
971  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color" )] );
972  }
973  else if ( props.contains( QStringLiteral( "color" ) ) )
974  {
975  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
976  }
977 
978  if ( props.contains( QStringLiteral( "shapeburst_color2" ) ) )
979  {
980  //pre 2.5 projects used "shapeburst_color2"
981  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color2" )] );
982  }
983  else if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
984  {
985  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
986  }
987  if ( props.contains( QStringLiteral( "blur_radius" ) ) )
988  {
989  blurRadius = props[QStringLiteral( "blur_radius" )].toInt();
990  }
991  if ( props.contains( QStringLiteral( "use_whole_shape" ) ) )
992  {
993  useWholeShape = props[QStringLiteral( "use_whole_shape" )].toInt();
994  }
995  if ( props.contains( QStringLiteral( "max_distance" ) ) )
996  {
997  maxDistance = props[QStringLiteral( "max_distance" )].toDouble();
998  }
999  if ( props.contains( QStringLiteral( "offset" ) ) )
1000  {
1001  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
1002  }
1003 
1004  //attempt to create color ramp from props
1005  QgsColorRamp *gradientRamp = nullptr;
1006  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
1007  {
1008  gradientRamp = QgsCptCityColorRamp::create( props );
1009  }
1010  else
1011  {
1012  gradientRamp = QgsGradientColorRamp::create( props );
1013  }
1014 
1015  //create a new shapeburst fill layer with desired properties
1016  QgsShapeburstFillSymbolLayer *sl = new QgsShapeburstFillSymbolLayer( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1017  sl->setOffset( offset );
1018  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1019  {
1020  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1021  }
1022  if ( props.contains( QStringLiteral( "distance_unit" ) ) )
1023  {
1024  sl->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "distance_unit" )] ) );
1025  }
1026  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1027  {
1028  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1029  }
1030  if ( props.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
1031  {
1032  sl->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "distance_map_unit_scale" )] ) );
1033  }
1034  if ( props.contains( QStringLiteral( "ignore_rings" ) ) )
1035  {
1036  sl->setIgnoreRings( props[QStringLiteral( "ignore_rings" )].toInt() );
1037  }
1038  if ( gradientRamp )
1039  {
1040  sl->setColorRamp( gradientRamp );
1041  }
1042 
1043  sl->restoreOldDataDefinedProperties( props );
1044 
1045  return sl;
1046 }
1047 
1049 {
1050  return QStringLiteral( "ShapeburstFill" );
1051 }
1052 
1054 {
1055  if ( mGradientRamp.get() == ramp )
1056  return;
1057 
1058  mGradientRamp.reset( ramp );
1059 }
1060 
1061 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QColor &color, QColor &color2, int &blurRadius, bool &useWholeShape,
1062  double &maxDistance, bool &ignoreRings )
1063 {
1064  //first gradient color
1065  color = mColor;
1067  {
1070  }
1071 
1072  //second gradient color
1073  color2 = mColor2;
1075  {
1078  }
1079 
1080  //blur radius
1081  blurRadius = mBlurRadius;
1083  {
1084  context.setOriginalValueVariable( mBlurRadius );
1086  }
1087 
1088  //use whole shape
1089  useWholeShape = mUseWholeShape;
1091  {
1092  context.setOriginalValueVariable( mUseWholeShape );
1094  }
1095 
1096  //max distance
1097  maxDistance = mMaxDistance;
1099  {
1100  context.setOriginalValueVariable( mMaxDistance );
1102  }
1103 
1104  //ignore rings
1105  ignoreRings = mIgnoreRings;
1107  {
1108  context.setOriginalValueVariable( mIgnoreRings );
1110  }
1111 
1112 }
1113 
1115 {
1116  //TODO - check this
1117  QColor selColor = context.renderContext().selectionColor();
1118  if ( ! SELECTION_IS_OPAQUE )
1119  selColor.setAlphaF( context.opacity() );
1120  mSelBrush = QBrush( selColor );
1121 }
1122 
1124 {
1125  Q_UNUSED( context );
1126 }
1127 
1128 void QgsShapeburstFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1129 {
1130  QPainter *p = context.renderContext().painter();
1131  if ( !p )
1132  {
1133  return;
1134  }
1135 
1136  if ( context.selected() )
1137  {
1138  //feature is selected, draw using selection style
1139  p->setBrush( mSelBrush );
1140  QPointF offset;
1141  if ( !mOffset.isNull() )
1142  {
1143  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1144  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1145  p->translate( offset );
1146  }
1147  _renderPolygon( p, points, rings, context );
1148  if ( !mOffset.isNull() )
1149  {
1150  p->translate( -offset );
1151  }
1152  return;
1153  }
1154 
1155  QColor color1, color2;
1156  int blurRadius;
1157  bool useWholeShape;
1158  double maxDistance;
1159  bool ignoreRings;
1160  //calculate data defined symbology
1161  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1162 
1163  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1164  int outputPixelMaxDist = 0;
1165  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1166  {
1167  //convert max distance to pixels
1168  outputPixelMaxDist = context.renderContext().convertToPainterUnits( maxDistance, mDistanceUnit, mDistanceMapUnitScale );
1169  }
1170 
1171  //if we are using the two color mode, create a gradient ramp
1172  std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1174  {
1175  twoColorGradientRamp = qgis::make_unique< QgsGradientColorRamp >( color1, color2 );
1176  }
1177 
1178  //no stroke for shapeburst fills
1179  p->setPen( QPen( Qt::NoPen ) );
1180 
1181  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1182  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1183  //create a QImage to draw shapeburst in
1184  int pointsWidth = static_cast< int >( std::round( points.boundingRect().width() ) );
1185  int pointsHeight = static_cast< int >( std::round( points.boundingRect().height() ) );
1186  int imWidth = pointsWidth + ( sideBuffer * 2 );
1187  int imHeight = pointsHeight + ( sideBuffer * 2 );
1188  std::unique_ptr< QImage > fillImage = qgis::make_unique< QImage >( imWidth,
1189  imHeight, QImage::Format_ARGB32_Premultiplied );
1190  if ( fillImage->isNull() )
1191  {
1192  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1193  return;
1194  }
1195 
1196  //also create an image to store the alpha channel
1197  std::unique_ptr< QImage > alphaImage = qgis::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1198  if ( alphaImage->isNull() )
1199  {
1200  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1201  return;
1202  }
1203 
1204  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1205  //polygon boundary. Since we don't care about pixels which fall outside the polygon, we start with a black image and then draw over it the
1206  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1207  fillImage->fill( Qt::black );
1208 
1209  //initially fill the alpha channel image with a transparent color
1210  alphaImage->fill( Qt::transparent );
1211 
1212  //now, draw the polygon in the alpha channel image
1213  QPainter imgPainter;
1214  imgPainter.begin( alphaImage.get() );
1215  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1216  imgPainter.setBrush( QBrush( Qt::white ) );
1217  imgPainter.setPen( QPen( Qt::black ) );
1218  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1219  _renderPolygon( &imgPainter, points, rings, context );
1220  imgPainter.end();
1221 
1222  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1223  //(this avoids calling _renderPolygon twice, since that can be slow)
1224  imgPainter.begin( fillImage.get() );
1225  if ( !ignoreRings )
1226  {
1227  imgPainter.drawImage( 0, 0, *alphaImage );
1228  }
1229  else
1230  {
1231  //using ignore rings mode, so the alpha image can't be used
1232  //directly as the alpha channel contains polygon rings and we need
1233  //to draw now without any rings
1234  imgPainter.setBrush( QBrush( Qt::white ) );
1235  imgPainter.setPen( QPen( Qt::black ) );
1236  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1237  _renderPolygon( &imgPainter, points, nullptr, context );
1238  }
1239  imgPainter.end();
1240 
1241  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1242  double *dtArray = distanceTransform( fillImage.get() );
1243 
1244  //copy distance transform values back to QImage, shading by appropriate color ramp
1245  dtArrayToQImage( dtArray, fillImage.get(), mColorType == QgsShapeburstFillSymbolLayer::SimpleTwoColor ? twoColorGradientRamp.get() : mGradientRamp.get(),
1246  context.opacity(), useWholeShape, outputPixelMaxDist );
1247 
1248  //clean up some variables
1249  delete [] dtArray;
1250 
1251  //apply blur if desired
1252  if ( blurRadius > 0 )
1253  {
1254  QgsSymbolLayerUtils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1255  }
1256 
1257  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1258  imgPainter.begin( fillImage.get() );
1259  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1260  imgPainter.drawImage( 0, 0, *alphaImage );
1261  imgPainter.end();
1262  //we're finished with the alpha channel image now
1263  alphaImage.reset();
1264 
1265  //draw shapeburst image in correct place in the destination painter
1266 
1267  p->save();
1268  QPointF offset;
1269  if ( !mOffset.isNull() )
1270  {
1271  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1272  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1273  p->translate( offset );
1274  }
1275 
1276  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1277 
1278  if ( !mOffset.isNull() )
1279  {
1280  p->translate( -offset );
1281  }
1282  p->restore();
1283 
1284 }
1285 
1286 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1287 
1288 /* distance transform of a 1d function using squared distance */
1289 void QgsShapeburstFillSymbolLayer::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1290 {
1291  int k = 0;
1292  v[0] = 0;
1293  z[0] = -INF;
1294  z[1] = + INF;
1295  for ( int q = 1; q <= n - 1; q++ )
1296  {
1297  double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1298  while ( s <= z[k] )
1299  {
1300  k--;
1301  s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1302  }
1303  k++;
1304  v[k] = q;
1305  z[k] = s;
1306  z[k + 1] = + INF;
1307  }
1308 
1309  k = 0;
1310  for ( int q = 0; q <= n - 1; q++ )
1311  {
1312  while ( z[k + 1] < q )
1313  k++;
1314  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1315  }
1316 }
1317 
1318 /* distance transform of 2d function using squared distance */
1319 void QgsShapeburstFillSymbolLayer::distanceTransform2d( double *im, int width, int height )
1320 {
1321  int maxDimension = std::max( width, height );
1322  double *f = new double[ maxDimension ];
1323  int *v = new int[ maxDimension ];
1324  double *z = new double[ maxDimension + 1 ];
1325  double *d = new double[ maxDimension ];
1326 
1327  // transform along columns
1328  for ( int x = 0; x < width; x++ )
1329  {
1330  for ( int y = 0; y < height; y++ )
1331  {
1332  f[y] = im[ x + y * width ];
1333  }
1334  distanceTransform1d( f, height, v, z, d );
1335  for ( int y = 0; y < height; y++ )
1336  {
1337  im[ x + y * width ] = d[y];
1338  }
1339  }
1340 
1341  // transform along rows
1342  for ( int y = 0; y < height; y++ )
1343  {
1344  for ( int x = 0; x < width; x++ )
1345  {
1346  f[x] = im[ x + y * width ];
1347  }
1348  distanceTransform1d( f, width, v, z, d );
1349  for ( int x = 0; x < width; x++ )
1350  {
1351  im[ x + y * width ] = d[x];
1352  }
1353  }
1354 
1355  delete [] d;
1356  delete [] f;
1357  delete [] v;
1358  delete [] z;
1359 }
1360 
1361 /* distance transform of a binary QImage */
1362 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im )
1363 {
1364  int width = im->width();
1365  int height = im->height();
1366 
1367  double *dtArray = new double[width * height];
1368 
1369  //load qImage to array
1370  QRgb tmpRgb;
1371  int idx = 0;
1372  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1373  {
1374  const QRgb *scanLine = reinterpret_cast< const QRgb * >( im->constScanLine( heightIndex ) );
1375  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1376  {
1377  tmpRgb = scanLine[widthIndex];
1378  if ( qRed( tmpRgb ) == 0 )
1379  {
1380  //black pixel, so zero distance
1381  dtArray[ idx ] = 0;
1382  }
1383  else
1384  {
1385  //white pixel, so initially set distance as infinite
1386  dtArray[ idx ] = INF;
1387  }
1388  idx++;
1389  }
1390  }
1391 
1392  //calculate squared distance transform
1393  distanceTransform2d( dtArray, width, height );
1394 
1395  return dtArray;
1396 }
1397 
1398 void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, QgsColorRamp *ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1399 {
1400  int width = im->width();
1401  int height = im->height();
1402 
1403  //find maximum distance value
1404  double maxDistanceValue;
1405 
1406  if ( useWholeShape )
1407  {
1408  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1409  double dtMaxValue = array[0];
1410  for ( int i = 1; i < ( width * height ); ++i )
1411  {
1412  if ( array[i] > dtMaxValue )
1413  {
1414  dtMaxValue = array[i];
1415  }
1416  }
1417 
1418  //values in distance transform are squared
1419  maxDistanceValue = std::sqrt( dtMaxValue );
1420  }
1421  else
1422  {
1423  //use max distance set in symbol properties
1424  maxDistanceValue = maxPixelDistance;
1425  }
1426 
1427  //update the pixels in the provided QImage
1428  int idx = 0;
1429  double squaredVal = 0;
1430  double pixVal = 0;
1431  QColor pixColor;
1432  bool layerHasAlpha = layerAlpha < 1.0;
1433 
1434  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1435  {
1436  QRgb *scanLine = reinterpret_cast< QRgb * >( im->scanLine( heightIndex ) );
1437  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1438  {
1439  //result of distance transform
1440  squaredVal = array[idx];
1441 
1442  //scale result to fit in the range [0, 1]
1443  if ( maxDistanceValue > 0 )
1444  {
1445  pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1446  }
1447  else
1448  {
1449  pixVal = 1.0;
1450  }
1451 
1452  //convert value to color from ramp
1453  pixColor = ramp->color( pixVal );
1454 
1455  int pixAlpha = pixColor.alpha();
1456  if ( ( layerHasAlpha ) || ( pixAlpha != 255 ) )
1457  {
1458  //apply layer's transparency to alpha value
1459  double alpha = pixAlpha * layerAlpha;
1460  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1461  QgsSymbolLayerUtils::premultiplyColor( pixColor, alpha );
1462  }
1463 
1464  scanLine[widthIndex] = pixColor.rgba();
1465  idx++;
1466  }
1467  }
1468 }
1469 
1471 {
1472  QgsStringMap map;
1473  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1474  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
1475  map[QStringLiteral( "color_type" )] = QString::number( mColorType );
1476  map[QStringLiteral( "blur_radius" )] = QString::number( mBlurRadius );
1477  map[QStringLiteral( "use_whole_shape" )] = QString::number( mUseWholeShape );
1478  map[QStringLiteral( "max_distance" )] = QString::number( mMaxDistance );
1479  map[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
1480  map[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
1481  map[QStringLiteral( "ignore_rings" )] = QString::number( mIgnoreRings );
1482  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1483  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1484  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1485  if ( mGradientRamp )
1486  {
1487  map.unite( mGradientRamp->properties() );
1488  }
1489 
1490  return map;
1491 }
1492 
1494 {
1495  QgsShapeburstFillSymbolLayer *sl = new QgsShapeburstFillSymbolLayer( mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1496  if ( mGradientRamp )
1497  {
1498  sl->setColorRamp( mGradientRamp->clone() );
1499  }
1500  sl->setDistanceUnit( mDistanceUnit );
1501  sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1502  sl->setIgnoreRings( mIgnoreRings );
1503  sl->setOffset( mOffset );
1504  sl->setOffsetUnit( mOffsetUnit );
1505  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1507  copyPaintEffect( sl );
1508  return sl;
1509 }
1510 
1512 {
1513  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1514  return offsetBleed;
1515 }
1516 
1518 {
1519  mDistanceUnit = unit;
1520  mOffsetUnit = unit;
1521 }
1522 
1524 {
1525  if ( mDistanceUnit == mOffsetUnit )
1526  {
1527  return mDistanceUnit;
1528  }
1530 }
1531 
1533 {
1534  mDistanceMapUnitScale = scale;
1535  mOffsetMapUnitScale = scale;
1536 }
1537 
1539 {
1540  if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1541  {
1542  return mDistanceMapUnitScale;
1543  }
1544  return QgsMapUnitScale();
1545 }
1546 
1547 
1548 //QgsImageFillSymbolLayer
1549 
1551 {
1552  setSubSymbol( new QgsLineSymbol() );
1553 }
1554 
1555 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1556 {
1557  QPainter *p = context.renderContext().painter();
1558  if ( !p )
1559  {
1560  return;
1561  }
1562 
1563  mNextAngle = mAngle;
1564  applyDataDefinedSettings( context );
1565 
1566  p->setPen( QPen( Qt::NoPen ) );
1567 
1568  QTransform bkTransform = mBrush.transform();
1570  {
1571  //transform brush to upper left corner of geometry bbox
1572  QPointF leftCorner = points.boundingRect().topLeft();
1573  QTransform t = mBrush.transform();
1574  t.translate( leftCorner.x(), leftCorner.y() );
1575  mBrush.setTransform( t );
1576  }
1577 
1578  if ( context.selected() )
1579  {
1580  QColor selColor = context.renderContext().selectionColor();
1581  // Alister - this doesn't seem to work here
1582  //if ( ! selectionIsOpaque )
1583  // selColor.setAlphaF( context.alpha() );
1584  p->setBrush( QBrush( selColor ) );
1585  _renderPolygon( p, points, rings, context );
1586  }
1587 
1588  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1589  {
1590  QTransform t = mBrush.transform();
1591  t.rotate( mNextAngle );
1592  mBrush.setTransform( t );
1593  }
1594  p->setBrush( mBrush );
1595  _renderPolygon( p, points, rings, context );
1596  if ( mStroke )
1597  {
1598  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1599  if ( rings )
1600  {
1601  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1602  for ( ; ringIt != rings->constEnd(); ++ringIt )
1603  {
1604  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1605  }
1606  }
1607  }
1608 
1609  mBrush.setTransform( bkTransform );
1610 }
1611 
1613 {
1614  if ( !symbol ) //unset current stroke
1615  {
1616  mStroke.reset( nullptr );
1617  return true;
1618  }
1619 
1620  if ( symbol->type() != QgsSymbol::Line )
1621  {
1622  delete symbol;
1623  return false;
1624  }
1625 
1626  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
1627  if ( lineSymbol )
1628  {
1629  mStroke.reset( lineSymbol );
1630  return true;
1631  }
1632 
1633  delete symbol;
1634  return false;
1635 }
1636 
1638 {
1639  mStrokeWidthUnit = unit;
1640 }
1641 
1643 {
1644  return mStrokeWidthUnit;
1645 }
1646 
1648 {
1649  mStrokeWidthMapUnitScale = scale;
1650 }
1651 
1653 {
1654  return mStrokeWidthMapUnitScale;
1655 }
1656 
1658 {
1659  if ( mStroke && mStroke->symbolLayer( 0 ) )
1660  {
1661  double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1662  return subLayerBleed;
1663  }
1664  return 0;
1665 }
1666 
1668 {
1669  double width = mStrokeWidth;
1671  {
1672  context.setOriginalValueVariable( mStrokeWidth );
1674  }
1675  return width * e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1676 }
1677 
1679 {
1680  Q_UNUSED( context );
1681  if ( !mStroke )
1682  {
1683  return QColor( Qt::black );
1684  }
1685  return mStroke->color();
1686 }
1687 
1689 {
1690  return Qt::SolidLine;
1691 #if 0
1692  if ( !mStroke )
1693  {
1694  return Qt::SolidLine;
1695  }
1696  else
1697  {
1698  return mStroke->dxfPenStyle();
1699  }
1700 #endif //0
1701 }
1702 
1703 QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1704 {
1705  QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
1706  if ( mStroke )
1707  attr.unite( mStroke->usedAttributes( context ) );
1708  return attr;
1709 }
1710 
1711 
1712 //QgsSVGFillSymbolLayer
1713 
1714 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString &svgFilePath, double width, double angle )
1716  , mPatternWidth( width )
1717  , mPatternWidthUnit( QgsUnitTypes::RenderMillimeters )
1718  , mSvgStrokeWidthUnit( QgsUnitTypes::RenderMillimeters )
1719 {
1720  setSvgFilePath( svgFilePath );
1721  mStrokeWidth = 0.3;
1722  mAngle = angle;
1723  mColor = QColor( 255, 255, 255 );
1724  mSvgStrokeColor = QColor( 35, 35, 35 );
1725  mSvgStrokeWidth = 0.2;
1726  setDefaultSvgParams();
1727  mSvgPattern = nullptr;
1728 }
1729 
1730 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double width, double angle )
1732  , mPatternWidth( width )
1733  , mPatternWidthUnit( QgsUnitTypes::RenderMillimeters )
1734  , mSvgData( svgData )
1735  , mSvgStrokeWidthUnit( QgsUnitTypes::RenderMillimeters )
1736 {
1737  storeViewBox();
1738  mStrokeWidth = 0.3;
1739  mAngle = angle;
1740  mColor = QColor( 255, 255, 255 );
1741  mSvgStrokeColor = QColor( 35, 35, 35 );
1742  mSvgStrokeWidth = 0.2;
1743  setSubSymbol( new QgsLineSymbol() );
1744  setDefaultSvgParams();
1745  mSvgPattern = nullptr;
1746 }
1747 
1749 {
1750  delete mSvgPattern;
1751 }
1752 
1754 {
1756  mPatternWidthUnit = unit;
1757  mSvgStrokeWidthUnit = unit;
1758  mStrokeWidthUnit = unit;
1759  mStroke->setOutputUnit( unit );
1760 }
1761 
1763 {
1765  if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit || mStrokeWidthUnit != unit )
1766  {
1768  }
1769  return unit;
1770 }
1771 
1773 {
1775  mPatternWidthMapUnitScale = scale;
1777  mStrokeWidthMapUnitScale = scale;
1778 }
1779 
1781 {
1785  {
1787  }
1788  return QgsMapUnitScale();
1789 }
1790 
1791 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString &svgPath )
1792 {
1794  storeViewBox();
1795 
1796  mSvgFilePath = svgPath;
1797  setDefaultSvgParams();
1798 }
1799 
1801 {
1802  QByteArray data;
1803  double width = 20;
1804  QString svgFilePath;
1805  double angle = 0.0;
1806 
1807  if ( properties.contains( QStringLiteral( "width" ) ) )
1808  {
1809  width = properties[QStringLiteral( "width" )].toDouble();
1810  }
1811  if ( properties.contains( QStringLiteral( "svgFile" ) ) )
1812  {
1813  svgFilePath = properties[QStringLiteral( "svgFile" )];
1814  }
1815  if ( properties.contains( QStringLiteral( "angle" ) ) )
1816  {
1817  angle = properties[QStringLiteral( "angle" )].toDouble();
1818  }
1819 
1820  QgsSVGFillSymbolLayer *symbolLayer = nullptr;
1821  if ( !svgFilePath.isEmpty() )
1822  {
1823  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1824  }
1825  else
1826  {
1827  if ( properties.contains( QStringLiteral( "data" ) ) )
1828  {
1829  data = QByteArray::fromHex( properties[QStringLiteral( "data" )].toLocal8Bit() );
1830  }
1831  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1832  }
1833 
1834  //svg parameters
1835  if ( properties.contains( QStringLiteral( "svgFillColor" ) ) )
1836  {
1837  //pre 2.5 projects used "svgFillColor"
1838  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgFillColor" )] ) );
1839  }
1840  else if ( properties.contains( QStringLiteral( "color" ) ) )
1841  {
1842  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
1843  }
1844  if ( properties.contains( QStringLiteral( "svgOutlineColor" ) ) )
1845  {
1846  //pre 2.5 projects used "svgOutlineColor"
1847  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgOutlineColor" )] ) );
1848  }
1849  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
1850  {
1851  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
1852  }
1853  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
1854  {
1855  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
1856  }
1857  if ( properties.contains( QStringLiteral( "svgOutlineWidth" ) ) )
1858  {
1859  //pre 2.5 projects used "svgOutlineWidth"
1860  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "svgOutlineWidth" )].toDouble() );
1861  }
1862  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
1863  {
1864  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
1865  }
1866  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
1867  {
1868  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
1869  }
1870 
1871  //units
1872  if ( properties.contains( QStringLiteral( "pattern_width_unit" ) ) )
1873  {
1874  symbolLayer->setPatternWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "pattern_width_unit" )] ) );
1875  }
1876  if ( properties.contains( QStringLiteral( "pattern_width_map_unit_scale" ) ) )
1877  {
1878  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "pattern_width_map_unit_scale" )] ) );
1879  }
1880  if ( properties.contains( QStringLiteral( "svg_outline_width_unit" ) ) )
1881  {
1882  symbolLayer->setSvgStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "svg_outline_width_unit" )] ) );
1883  }
1884  if ( properties.contains( QStringLiteral( "svg_outline_width_map_unit_scale" ) ) )
1885  {
1886  symbolLayer->setSvgStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "svg_outline_width_map_unit_scale" )] ) );
1887  }
1888  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
1889  {
1890  symbolLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
1891  }
1892  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1893  {
1894  symbolLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1895  }
1896 
1897  symbolLayer->restoreOldDataDefinedProperties( properties );
1898 
1899  return symbolLayer;
1900 }
1901 
1903 {
1904  QgsStringMap::iterator it = properties.find( QStringLiteral( "svgFile" ) );
1905  if ( it != properties.end() )
1906  {
1907  if ( saving )
1908  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1909  else
1910  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1911  }
1912 }
1913 
1915 {
1916  return QStringLiteral( "SVGFill" );
1917 }
1918 
1919 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush, const QString &svgFilePath, double patternWidth, QgsUnitTypes::RenderUnit patternWidthUnit,
1920  const QColor &svgFillColor, const QColor &svgStrokeColor, double svgStrokeWidth,
1923 {
1924  if ( mSvgViewBox.isNull() )
1925  {
1926  return;
1927  }
1928 
1929  delete mSvgPattern;
1930  mSvgPattern = nullptr;
1931  double size = context.renderContext().convertToPainterUnits( patternWidth, patternWidthUnit, patternWidthMapUnitScale );
1932 
1933  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1934  {
1935  mSvgPattern = new QImage();
1936  brush.setTextureImage( *mSvgPattern );
1937  }
1938  else
1939  {
1940  bool fitsInCache = true;
1941  double strokeWidth = context.renderContext().convertToPainterUnits( svgStrokeWidth, svgStrokeWidthUnit, svgStrokeWidthMapUnitScale );
1942  QImage patternImage = QgsApplication::svgCache()->svgAsImage( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1943  context.renderContext().scaleFactor(), fitsInCache );
1944  if ( !fitsInCache )
1945  {
1946  QPicture patternPict = QgsApplication::svgCache()->svgAsPicture( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1947  context.renderContext().scaleFactor() );
1948  double hwRatio = 1.0;
1949  if ( patternPict.width() > 0 )
1950  {
1951  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1952  }
1953  mSvgPattern = new QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1954  mSvgPattern->fill( 0 ); // transparent background
1955 
1956  QPainter p( mSvgPattern );
1957  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1958  }
1959 
1960  QTransform brushTransform;
1961  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
1962  {
1963  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1964  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
1965  brush.setTextureImage( transparentImage );
1966  }
1967  else
1968  {
1969  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1970  }
1971  brush.setTransform( brushTransform );
1972  }
1973 }
1974 
1976 {
1977 
1979 
1980  if ( mStroke )
1981  {
1982  mStroke->startRender( context.renderContext(), context.fields() );
1983  }
1984 }
1985 
1987 {
1988  if ( mStroke )
1989  {
1990  mStroke->stopRender( context.renderContext() );
1991  }
1992 }
1993 
1995 {
1996  QgsStringMap map;
1997  if ( !mSvgFilePath.isEmpty() )
1998  {
1999  map.insert( QStringLiteral( "svgFile" ), mSvgFilePath );
2000  }
2001  else
2002  {
2003  map.insert( QStringLiteral( "data" ), QString( mSvgData.toHex() ) );
2004  }
2005 
2006  map.insert( QStringLiteral( "width" ), QString::number( mPatternWidth ) );
2007  map.insert( QStringLiteral( "angle" ), QString::number( mAngle ) );
2008 
2009  //svg parameters
2010  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2011  map.insert( QStringLiteral( "outline_color" ), QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2012  map.insert( QStringLiteral( "outline_width" ), QString::number( mSvgStrokeWidth ) );
2013 
2014  //units
2015  map.insert( QStringLiteral( "pattern_width_unit" ), QgsUnitTypes::encodeUnit( mPatternWidthUnit ) );
2016  map.insert( QStringLiteral( "pattern_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2017  map.insert( QStringLiteral( "svg_outline_width_unit" ), QgsUnitTypes::encodeUnit( mSvgStrokeWidthUnit ) );
2018  map.insert( QStringLiteral( "svg_outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSvgStrokeWidthMapUnitScale ) );
2019  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2020  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2021  return map;
2022 }
2023 
2025 {
2026  QgsSVGFillSymbolLayer *clonedLayer = nullptr;
2027  if ( !mSvgFilePath.isEmpty() )
2028  {
2029  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
2030  clonedLayer->setSvgFillColor( mColor );
2031  clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2032  clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2033  }
2034  else
2035  {
2036  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
2037  }
2038 
2039  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2043  clonedLayer->setStrokeWidthUnit( mStrokeWidthUnit );
2045 
2046  if ( mStroke )
2047  {
2048  clonedLayer->setSubSymbol( mStroke->clone() );
2049  }
2050  copyDataDefinedProperties( clonedLayer );
2051  copyPaintEffect( clonedLayer );
2052  return clonedLayer;
2053 }
2054 
2055 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2056 {
2057  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2058  if ( !props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ).isEmpty() )
2059  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ) );
2060  element.appendChild( symbolizerElem );
2061 
2062  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QLatin1String( "" ) ) );
2063 
2064  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2065  symbolizerElem.appendChild( fillElem );
2066 
2067  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2068  fillElem.appendChild( graphicFillElem );
2069 
2070  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2071  graphicFillElem.appendChild( graphicElem );
2072 
2073  if ( !mSvgFilePath.isEmpty() )
2074  {
2075  // encode a parametric SVG reference
2078  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mSvgFilePath, mColor, patternWidth, mSvgStrokeColor, strokeWidth );
2079  }
2080  else
2081  {
2082  // TODO: create svg from data
2083  // <se:InlineContent>
2084  symbolizerElem.appendChild( doc.createComment( QStringLiteral( "SVG from data not implemented yet" ) ) );
2085  }
2086 
2087  // <Rotation>
2088  QString angleFunc;
2089  bool ok;
2090  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2091  if ( !ok )
2092  {
2093  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2094  }
2095  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2096  {
2097  angleFunc = QString::number( angle + mAngle );
2098  }
2099  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2100 
2101  if ( mStroke )
2102  {
2103  // the stroke sub symbol should be stored within the Stroke element,
2104  // but it will be stored in a separated LineSymbolizer because it could
2105  // have more than one layer
2106  mStroke->toSld( doc, element, props );
2107  }
2108 }
2109 
2111 {
2112  QgsDebugMsg( "Entered." );
2113 
2114  QString path, mimeType;
2115  QColor fillColor, strokeColor;
2116  Qt::PenStyle penStyle;
2117  double size, strokeWidth;
2118 
2119  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2120  if ( fillElem.isNull() )
2121  return nullptr;
2122 
2123  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2124  if ( graphicFillElem.isNull() )
2125  return nullptr;
2126 
2127  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2128  if ( graphicElem.isNull() )
2129  return nullptr;
2130 
2131  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2132  return nullptr;
2133 
2134  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2135  return nullptr;
2136 
2137  QgsSymbolLayerUtils::lineFromSld( graphicElem, penStyle, strokeColor, strokeWidth );
2138 
2139  QString uom = element.attribute( QStringLiteral( "uom" ) );
2140  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2141  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
2142 
2143  double angle = 0.0;
2144  QString angleFunc;
2145  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2146  {
2147  bool ok;
2148  double d = angleFunc.toDouble( &ok );
2149  if ( ok )
2150  angle = d;
2151  }
2152 
2153  QgsSVGFillSymbolLayer *sl = new QgsSVGFillSymbolLayer( path, size, angle );
2154  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2155  sl->setSvgFillColor( fillColor );
2156  sl->setSvgStrokeColor( strokeColor );
2157  sl->setSvgStrokeWidth( strokeWidth );
2158 
2159  // try to get the stroke
2160  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2161  if ( !strokeElem.isNull() )
2162  {
2164  if ( l )
2165  {
2166  QgsSymbolLayerList layers;
2167  layers.append( l );
2168  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2169  }
2170  }
2171 
2172  return sl;
2173 }
2174 
2176 {
2180  {
2181  return; //no data defined settings
2182  }
2183 
2185  {
2186  context.setOriginalValueVariable( mAngle );
2188  }
2189 
2190  double width = mPatternWidth;
2192  {
2195  }
2196  QString svgFile = mSvgFilePath;
2198  {
2201  context.renderContext().pathResolver() );
2202  }
2203  QColor svgFillColor = mColor;
2205  {
2208  }
2211  {
2214  }
2215  double strokeWidth = mSvgStrokeWidth;
2217  {
2220  }
2221  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgStrokeColor, strokeWidth,
2223 
2224 }
2225 
2226 void QgsSVGFillSymbolLayer::storeViewBox()
2227 {
2228  if ( !mSvgData.isEmpty() )
2229  {
2230  QSvgRenderer r( mSvgData );
2231  if ( r.isValid() )
2232  {
2233  mSvgViewBox = r.viewBoxF();
2234  return;
2235  }
2236  }
2237 
2238  mSvgViewBox = QRectF();
2239 }
2240 
2241 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2242 {
2243  if ( mSvgFilePath.isEmpty() )
2244  {
2245  return;
2246  }
2247 
2248  bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2249  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2250  QColor defaultFillColor, defaultStrokeColor;
2251  double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2252  QgsApplication::svgCache()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2253  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2254  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2255  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2256  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2257 
2258  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2259  double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2260 
2261  if ( hasDefaultFillColor )
2262  {
2263  mColor = defaultFillColor;
2264  mColor.setAlphaF( newFillOpacity );
2265  }
2266  if ( hasDefaultFillOpacity )
2267  {
2268  mColor.setAlphaF( defaultFillOpacity );
2269  }
2270  if ( hasDefaultStrokeColor )
2271  {
2272  mSvgStrokeColor = defaultStrokeColor;
2273  mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2274  }
2275  if ( hasDefaultStrokeOpacity )
2276  {
2277  mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2278  }
2279  if ( hasDefaultStrokeWidth )
2280  {
2281  mSvgStrokeWidth = defaultStrokeWidth;
2282  }
2283 }
2284 
2285 
2288 {
2289  setSubSymbol( new QgsLineSymbol() );
2290  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2291 }
2292 
2294 {
2295  mFillLineSymbol->setWidth( w );
2296  mLineWidth = w;
2297 }
2298 
2300 {
2301  mFillLineSymbol->setColor( c );
2302  mColor = c;
2303 }
2304 
2306 {
2307  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2308 }
2309 
2311 {
2312  delete mFillLineSymbol;
2313 }
2314 
2316 {
2317  if ( !symbol )
2318  {
2319  return false;
2320  }
2321 
2322  if ( symbol->type() == QgsSymbol::Line )
2323  {
2324  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
2325  if ( lineSymbol )
2326  {
2327  delete mFillLineSymbol;
2328  mFillLineSymbol = lineSymbol;
2329 
2330  return true;
2331  }
2332  }
2333  delete symbol;
2334  return false;
2335 }
2336 
2338 {
2339  return mFillLineSymbol;
2340 }
2341 
2343 {
2344  QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
2345  if ( mFillLineSymbol )
2346  attr.unite( mFillLineSymbol->usedAttributes( context ) );
2347  return attr;
2348 }
2349 
2351 {
2352  return 0;
2353 }
2354 
2356 {
2358  mDistanceUnit = unit;
2359  mLineWidthUnit = unit;
2360  mOffsetUnit = unit;
2361 }
2362 
2364 {
2366  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2367  {
2369  }
2370  return unit;
2371 }
2372 
2374 {
2376  mDistanceMapUnitScale = scale;
2377  mLineWidthMapUnitScale = scale;
2378  mOffsetMapUnitScale = scale;
2379 }
2380 
2382 {
2386  {
2387  return mDistanceMapUnitScale;
2388  }
2389  return QgsMapUnitScale();
2390 }
2391 
2393 {
2395 
2396  //default values
2397  double lineAngle = 45;
2398  double distance = 5;
2399  double lineWidth = 0.5;
2400  QColor color( Qt::black );
2401  double offset = 0.0;
2402 
2403  if ( properties.contains( QStringLiteral( "lineangle" ) ) )
2404  {
2405  //pre 2.5 projects used "lineangle"
2406  lineAngle = properties[QStringLiteral( "lineangle" )].toDouble();
2407  }
2408  else if ( properties.contains( QStringLiteral( "angle" ) ) )
2409  {
2410  lineAngle = properties[QStringLiteral( "angle" )].toDouble();
2411  }
2412  patternLayer->setLineAngle( lineAngle );
2413 
2414  if ( properties.contains( QStringLiteral( "distance" ) ) )
2415  {
2416  distance = properties[QStringLiteral( "distance" )].toDouble();
2417  }
2418  patternLayer->setDistance( distance );
2419 
2420  if ( properties.contains( QStringLiteral( "linewidth" ) ) )
2421  {
2422  //pre 2.5 projects used "linewidth"
2423  lineWidth = properties[QStringLiteral( "linewidth" )].toDouble();
2424  }
2425  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
2426  {
2427  lineWidth = properties[QStringLiteral( "outline_width" )].toDouble();
2428  }
2429  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
2430  {
2431  lineWidth = properties[QStringLiteral( "line_width" )].toDouble();
2432  }
2433  patternLayer->setLineWidth( lineWidth );
2434 
2435  if ( properties.contains( QStringLiteral( "color" ) ) )
2436  {
2437  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] );
2438  }
2439  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
2440  {
2441  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] );
2442  }
2443  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
2444  {
2445  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] );
2446  }
2447  patternLayer->setColor( color );
2448 
2449  if ( properties.contains( QStringLiteral( "offset" ) ) )
2450  {
2451  offset = properties[QStringLiteral( "offset" )].toDouble();
2452  }
2453  patternLayer->setOffset( offset );
2454 
2455 
2456  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
2457  {
2458  patternLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )] ) );
2459  }
2460  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
2461  {
2462  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )] ) );
2463  }
2464  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
2465  {
2466  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
2467  }
2468  else if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2469  {
2470  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2471  }
2472  if ( properties.contains( QStringLiteral( "line_width_map_unit_scale" ) ) )
2473  {
2474  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "line_width_map_unit_scale" )] ) );
2475  }
2476  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2477  {
2478  patternLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
2479  }
2480  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2481  {
2482  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
2483  }
2484  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2485  {
2486  patternLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2487  }
2488  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2489  {
2490  patternLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2491  }
2492 
2493  patternLayer->restoreOldDataDefinedProperties( properties );
2494 
2495  return patternLayer;
2496 }
2497 
2499 {
2500  return QStringLiteral( "LinePatternFill" );
2501 }
2502 
2503 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double lineAngle, double distance )
2504 {
2505  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2506 
2507  if ( !mFillLineSymbol )
2508  {
2509  return;
2510  }
2511  // We have to make a copy because marker intervals will have to be adjusted
2512  QgsLineSymbol *fillLineSymbol = mFillLineSymbol->clone();
2513  if ( !fillLineSymbol )
2514  {
2515  return;
2516  }
2517 
2518  const QgsRenderContext &ctx = context.renderContext();
2519  //double strokePixelWidth = lineWidth * QgsSymbolLayerUtils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2520  double outputPixelDist = ctx.convertToPainterUnits( distance, mDistanceUnit, mDistanceMapUnitScale );
2521  double outputPixelOffset = ctx.convertToPainterUnits( mOffset, mOffsetUnit, mOffsetMapUnitScale );
2522 
2523  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2524  // For marker lines we have to get markers interval.
2525  double outputPixelBleed = 0;
2526  double outputPixelInterval = 0; // maximum interval
2527  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2528  {
2529  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2530  double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() );
2531  outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2532 
2533  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2534  if ( markerLineLayer )
2535  {
2536  double outputPixelLayerInterval = ctx.convertToPainterUnits( markerLineLayer->interval(), markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2537 
2538  // There may be multiple marker lines with different intervals.
2539  // In theory we should find the least common multiple, but that could be too
2540  // big (multiplication of intervals in the worst case).
2541  // Because patterns without small common interval would look strange, we
2542  // believe that the longest interval should usually be sufficient.
2543  outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2544  }
2545  }
2546 
2547  if ( outputPixelInterval > 0 )
2548  {
2549  // We have to adjust marker intervals to integer pixel size to get
2550  // repeatable pattern.
2551  double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2552  outputPixelInterval = std::round( outputPixelInterval );
2553 
2554  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2555  {
2556  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2557 
2558  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2559  if ( markerLineLayer )
2560  {
2561  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2562  }
2563  }
2564  }
2565 
2566  //create image
2567  int height, width;
2568  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2569  {
2570  height = outputPixelDist;
2571  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2572  }
2573  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2574  {
2575  width = outputPixelDist;
2576  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2577  }
2578  else
2579  {
2580  height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2581  width = outputPixelDist / std::sin( lineAngle * M_PI / 180 );
2582 
2583  // recalculate real angle and distance after rounding to pixels
2584  lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2585  if ( lineAngle < 0 )
2586  {
2587  lineAngle += 360.;
2588  }
2589 
2590  height = std::abs( height );
2591  width = std::abs( width );
2592 
2593  outputPixelDist = height * std::cos( lineAngle * M_PI / 180 );
2594 
2595  // Round offset to correspond to one pixel height, otherwise lines may
2596  // be shifted on tile border if offset falls close to pixel center
2597  int offsetHeight = std::round( std::fabs( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) );
2598  outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 );
2599  }
2600 
2601  //depending on the angle, we might need to render into a larger image and use a subset of it
2602  double dx = 0;
2603  double dy = 0;
2604 
2605  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2606  // thus we add integer multiplications of width and height covering the bleed
2607  int bufferMulti = std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) );
2608 
2609  // Always buffer at least once so that center of line marker in upper right corner
2610  // does not fall outside due to representation error
2611  bufferMulti = std::max( bufferMulti, 1 );
2612 
2613  int xBuffer = width * bufferMulti;
2614  int yBuffer = height * bufferMulti;
2615  int innerWidth = width;
2616  int innerHeight = height;
2617  width += 2 * xBuffer;
2618  height += 2 * yBuffer;
2619 
2620  //protect from zero width/height image and symbol layer from eating too much memory
2621  if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2622  {
2623  return;
2624  }
2625 
2626  QImage patternImage( width, height, QImage::Format_ARGB32 );
2627  patternImage.fill( 0 );
2628 
2629  QPointF p1, p2, p3, p4, p5, p6;
2630  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2631  {
2632  p1 = QPointF( 0, yBuffer );
2633  p2 = QPointF( width, yBuffer );
2634  p3 = QPointF( 0, yBuffer + innerHeight );
2635  p4 = QPointF( width, yBuffer + innerHeight );
2636  }
2637  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2638  {
2639  p1 = QPointF( xBuffer, height );
2640  p2 = QPointF( xBuffer, 0 );
2641  p3 = QPointF( xBuffer + innerWidth, height );
2642  p4 = QPointF( xBuffer + innerWidth, 0 );
2643  }
2644  else if ( lineAngle > 0 && lineAngle < 90 )
2645  {
2646  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2647  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2648  p1 = QPointF( 0, height );
2649  p2 = QPointF( width, 0 );
2650  p3 = QPointF( -dx, height - dy );
2651  p4 = QPointF( width - dx, -dy );
2652  p5 = QPointF( dx, height + dy );
2653  p6 = QPointF( width + dx, dy );
2654  }
2655  else if ( lineAngle > 180 && lineAngle < 270 )
2656  {
2657  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2658  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2659  p1 = QPointF( width, 0 );
2660  p2 = QPointF( 0, height );
2661  p3 = QPointF( width - dx, -dy );
2662  p4 = QPointF( -dx, height - dy );
2663  p5 = QPointF( width + dx, dy );
2664  p6 = QPointF( dx, height + dy );
2665  }
2666  else if ( lineAngle > 90 && lineAngle < 180 )
2667  {
2668  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2669  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2670  p1 = QPointF( 0, 0 );
2671  p2 = QPointF( width, height );
2672  p5 = QPointF( dx, -dy );
2673  p6 = QPointF( width + dx, height - dy );
2674  p3 = QPointF( -dx, dy );
2675  p4 = QPointF( width - dx, height + dy );
2676  }
2677  else if ( lineAngle > 270 && lineAngle < 360 )
2678  {
2679  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2680  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2681  p1 = QPointF( width, height );
2682  p2 = QPointF( 0, 0 );
2683  p5 = QPointF( width + dx, height - dy );
2684  p6 = QPointF( dx, -dy );
2685  p3 = QPointF( width - dx, height + dy );
2686  p4 = QPointF( -dx, dy );
2687  }
2688 
2689  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2690  {
2691  QPointF tempPt;
2692  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2693  p3 = QPointF( tempPt.x(), tempPt.y() );
2694  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2695  p4 = QPointF( tempPt.x(), tempPt.y() );
2696  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2697  p5 = QPointF( tempPt.x(), tempPt.y() );
2698  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2699  p6 = QPointF( tempPt.x(), tempPt.y() );
2700 
2701  //update p1, p2 last
2702  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2703  p1 = QPointF( tempPt.x(), tempPt.y() );
2704  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2705  p2 = QPointF( tempPt.x(), tempPt.y() );
2706  }
2707 
2708  QPainter p( &patternImage );
2709 
2710 #if 0
2711  // DEBUG: Draw rectangle
2712  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2713  QPen pen( QColor( Qt::black ) );
2714  pen.setWidthF( 0.1 );
2715  pen.setCapStyle( Qt::FlatCap );
2716  p.setPen( pen );
2717 
2718  // To see this rectangle, comment buffer cut below.
2719  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2720  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2721  p.drawPolygon( polygon );
2722 
2723  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 );
2724  p.drawPolygon( polygon );
2725 #endif
2726 
2727  // Use antialiasing because without antialiasing lines are rendered to the
2728  // right and below the mathematically defined points (not symmetrical)
2729  // and such tiles become useless for are filling
2730  p.setRenderHint( QPainter::Antialiasing, true );
2731 
2732  // line rendering needs context for drawing on patternImage
2733  QgsRenderContext lineRenderContext;
2734  lineRenderContext.setPainter( &p );
2735  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
2737  lineRenderContext.setMapToPixel( mtp );
2738  lineRenderContext.setForceVectorOutput( false );
2739  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2740 
2741  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2742 
2743  QVector<QPolygonF> polygons;
2744  polygons.append( QPolygonF() << p1 << p2 );
2745  polygons.append( QPolygonF() << p3 << p4 );
2746  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2747  {
2748  polygons.append( QPolygonF() << p5 << p6 );
2749  }
2750 
2751  Q_FOREACH ( const QPolygonF &polygon, polygons )
2752  {
2753  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2754  }
2755 
2756  fillLineSymbol->stopRender( lineRenderContext );
2757  p.end();
2758 
2759  // Cut off the buffer
2760  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2761 
2762  //set image to mBrush
2763  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2764  {
2765  QImage transparentImage = patternImage.copy();
2766  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2767  brush.setTextureImage( transparentImage );
2768  }
2769  else
2770  {
2771  brush.setTextureImage( patternImage );
2772  }
2773 
2774  QTransform brushTransform;
2775  brush.setTransform( brushTransform );
2776 
2777  delete fillLineSymbol;
2778 }
2779 
2781 {
2782  applyPattern( context, mBrush, mLineAngle, mDistance );
2783 
2784  if ( mFillLineSymbol )
2785  {
2786  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2787  }
2788 }
2789 
2791 {
2792  if ( mFillLineSymbol )
2793  {
2794  mFillLineSymbol->stopRender( context.renderContext() );
2795  }
2796 }
2797 
2799 {
2800  QgsStringMap map;
2801  map.insert( QStringLiteral( "angle" ), QString::number( mLineAngle ) );
2802  map.insert( QStringLiteral( "distance" ), QString::number( mDistance ) );
2803  map.insert( QStringLiteral( "line_width" ), QString::number( mLineWidth ) );
2804  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2805  map.insert( QStringLiteral( "offset" ), QString::number( mOffset ) );
2806  map.insert( QStringLiteral( "distance_unit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
2807  map.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineWidthUnit ) );
2808  map.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
2809  map.insert( QStringLiteral( "distance_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2810  map.insert( QStringLiteral( "line_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2811  map.insert( QStringLiteral( "offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2812  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2813  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2814  return map;
2815 }
2816 
2818 {
2820  if ( mFillLineSymbol )
2821  {
2822  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2823  }
2824  copyPaintEffect( clonedLayer );
2825  copyDataDefinedProperties( clonedLayer );
2826  return clonedLayer;
2827 }
2828 
2829 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2830 {
2831  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2832  if ( !props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ).isEmpty() )
2833  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ) );
2834  element.appendChild( symbolizerElem );
2835 
2836  // <Geometry>
2837  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QLatin1String( "" ) ) );
2838 
2839  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2840  symbolizerElem.appendChild( fillElem );
2841 
2842  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2843  fillElem.appendChild( graphicFillElem );
2844 
2845  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2846  graphicFillElem.appendChild( graphicElem );
2847 
2848  //line properties must be inside the graphic definition
2849  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2850  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2851  lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2853  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "horline" ), QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2854 
2855  // <Rotation>
2856  QString angleFunc;
2857  bool ok;
2858  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2859  if ( !ok )
2860  {
2861  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mLineAngle );
2862  }
2863  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2864  {
2865  angleFunc = QString::number( angle + mLineAngle );
2866  }
2867  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2868 
2869  // <se:Displacement>
2870  QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2871  lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
2872  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
2873 }
2874 
2875 QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFactor ) const
2876 {
2877  QString featureStyle;
2878  featureStyle.append( "Brush(" );
2879  featureStyle.append( QStringLiteral( "fc:%1" ).arg( mColor.name() ) );
2880  featureStyle.append( QStringLiteral( ",bc:%1" ).arg( QStringLiteral( "#00000000" ) ) ); //transparent background
2881  featureStyle.append( ",id:\"ogr-brush-2\"" );
2882  featureStyle.append( QStringLiteral( ",a:%1" ).arg( mLineAngle ) );
2883  featureStyle.append( QStringLiteral( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2884  featureStyle.append( ",dx:0mm" );
2885  featureStyle.append( QStringLiteral( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2886  featureStyle.append( ')' );
2887  return featureStyle;
2888 }
2889 
2891 {
2893  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2894  {
2895  return; //no data defined settings
2896  }
2897 
2898  double lineAngle = mLineAngle;
2900  {
2903  }
2904  double distance = mDistance;
2906  {
2909  }
2910  applyPattern( context, mBrush, lineAngle, distance );
2911 }
2912 
2914 {
2915  QgsDebugMsg( "Entered." );
2916 
2917  QString name;
2918  QColor fillColor, lineColor;
2919  double size, lineWidth;
2920  Qt::PenStyle lineStyle;
2921 
2922  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2923  if ( fillElem.isNull() )
2924  return nullptr;
2925 
2926  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2927  if ( graphicFillElem.isNull() )
2928  return nullptr;
2929 
2930  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2931  if ( graphicElem.isNull() )
2932  return nullptr;
2933 
2934  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2935  return nullptr;
2936 
2937  if ( name != QLatin1String( "horline" ) )
2938  return nullptr;
2939 
2940  double angle = 0.0;
2941  QString angleFunc;
2942  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2943  {
2944  bool ok;
2945  double d = angleFunc.toDouble( &ok );
2946  if ( ok )
2947  angle = d;
2948  }
2949 
2950  double offset = 0.0;
2951  QPointF vectOffset;
2952  if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) )
2953  {
2954  offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
2955  }
2956 
2957  QString uom = element.attribute( QStringLiteral( "uom" ) );
2958  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2959  lineWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, lineWidth );
2960 
2962  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2963  sl->setColor( lineColor );
2964  sl->setLineWidth( lineWidth );
2965  sl->setLineAngle( angle );
2966  sl->setOffset( offset );
2967  sl->setDistance( size );
2968 
2969  // try to get the stroke
2970  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2971  if ( !strokeElem.isNull() )
2972  {
2974  if ( l )
2975  {
2976  QgsSymbolLayerList layers;
2977  layers.append( l );
2978  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2979  }
2980  }
2981 
2982  return sl;
2983 }
2984 
2985 
2987 
2990 {
2991  mDistanceX = 15;
2992  mDistanceY = 15;
2993  mDisplacementX = 0;
2994  mDisplacementY = 0;
2995  setSubSymbol( new QgsMarkerSymbol() );
2996  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2997 }
2998 
3000 {
3001  delete mMarkerSymbol;
3002 }
3003 
3005 {
3007  mDistanceXUnit = unit;
3008  mDistanceYUnit = unit;
3009  mDisplacementXUnit = unit;
3010  mDisplacementYUnit = unit;
3011  if ( mMarkerSymbol )
3012  {
3013  mMarkerSymbol->setOutputUnit( unit );
3014  }
3015 
3016 }
3017 
3019 {
3021  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3022  {
3024  }
3025  return unit;
3026 }
3027 
3029 {
3031  mDistanceXMapUnitScale = scale;
3032  mDistanceYMapUnitScale = scale;
3035 }
3036 
3038 {
3043  {
3044  return mDistanceXMapUnitScale;
3045  }
3046  return QgsMapUnitScale();
3047 }
3048 
3050 {
3052  if ( properties.contains( QStringLiteral( "distance_x" ) ) )
3053  {
3054  layer->setDistanceX( properties[QStringLiteral( "distance_x" )].toDouble() );
3055  }
3056  if ( properties.contains( QStringLiteral( "distance_y" ) ) )
3057  {
3058  layer->setDistanceY( properties[QStringLiteral( "distance_y" )].toDouble() );
3059  }
3060  if ( properties.contains( QStringLiteral( "displacement_x" ) ) )
3061  {
3062  layer->setDisplacementX( properties[QStringLiteral( "displacement_x" )].toDouble() );
3063  }
3064  if ( properties.contains( QStringLiteral( "displacement_y" ) ) )
3065  {
3066  layer->setDisplacementY( properties[QStringLiteral( "displacement_y" )].toDouble() );
3067  }
3068 
3069  if ( properties.contains( QStringLiteral( "distance_x_unit" ) ) )
3070  {
3071  layer->setDistanceXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_x_unit" )] ) );
3072  }
3073  if ( properties.contains( QStringLiteral( "distance_x_map_unit_scale" ) ) )
3074  {
3075  layer->setDistanceXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_x_map_unit_scale" )] ) );
3076  }
3077  if ( properties.contains( QStringLiteral( "distance_y_unit" ) ) )
3078  {
3079  layer->setDistanceYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_y_unit" )] ) );
3080  }
3081  if ( properties.contains( QStringLiteral( "distance_y_map_unit_scale" ) ) )
3082  {
3083  layer->setDistanceYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_y_map_unit_scale" )] ) );
3084  }
3085  if ( properties.contains( QStringLiteral( "displacement_x_unit" ) ) )
3086  {
3087  layer->setDisplacementXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_x_unit" )] ) );
3088  }
3089  if ( properties.contains( QStringLiteral( "displacement_x_map_unit_scale" ) ) )
3090  {
3091  layer->setDisplacementXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_x_map_unit_scale" )] ) );
3092  }
3093  if ( properties.contains( QStringLiteral( "displacement_y_unit" ) ) )
3094  {
3095  layer->setDisplacementYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_y_unit" )] ) );
3096  }
3097  if ( properties.contains( QStringLiteral( "displacement_y_map_unit_scale" ) ) )
3098  {
3099  layer->setDisplacementYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_y_map_unit_scale" )] ) );
3100  }
3101  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
3102  {
3103  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
3104  }
3105  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3106  {
3107  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3108  }
3109 
3110  layer->restoreOldDataDefinedProperties( properties );
3111 
3112  return layer;
3113 }
3114 
3116 {
3117  return QStringLiteral( "PointPatternFill" );
3118 }
3119 
3120 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double distanceX, double distanceY,
3121  double displacementX, double displacementY )
3122 {
3123  //render 3 rows and columns in one go to easily incorporate displacement
3124  const QgsRenderContext &ctx = context.renderContext();
3127 
3128  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3129  {
3130  QImage img;
3131  brush.setTextureImage( img );
3132  return;
3133  }
3134 
3135  QImage patternImage( width, height, QImage::Format_ARGB32 );
3136  patternImage.fill( 0 );
3137 
3138  if ( mMarkerSymbol )
3139  {
3140  QPainter p( &patternImage );
3141 
3142  //marker rendering needs context for drawing on patternImage
3143  QgsRenderContext pointRenderContext;
3144  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3145  pointRenderContext.setPainter( &p );
3146  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
3148  {
3149  pointRenderContext.setFlag( QgsRenderContext::Antialiasing, true );
3150  p.setRenderHint( QPainter::Antialiasing, true );
3151  }
3153  pointRenderContext.setMapToPixel( mtp );
3154  pointRenderContext.setForceVectorOutput( false );
3155  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3156 
3157  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3158 
3159  //render corner points
3160  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3161  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3162  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3163  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3164 
3165  //render displaced points
3167  double displacementPixelY = ctx.convertToPainterUnits( displacementY, mDisplacementYUnit, mDisplacementYMapUnitScale );
3168  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3169  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3170  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3171  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3172  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3173 
3174  mMarkerSymbol->stopRender( pointRenderContext );
3175  }
3176 
3177  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
3178  {
3179  QImage transparentImage = patternImage.copy();
3180  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
3181  brush.setTextureImage( transparentImage );
3182  }
3183  else
3184  {
3185  brush.setTextureImage( patternImage );
3186  }
3187  QTransform brushTransform;
3188  brush.setTransform( brushTransform );
3189 }
3190 
3192 {
3193  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3194 
3195  if ( mStroke )
3196  {
3197  mStroke->startRender( context.renderContext(), context.fields() );
3198  }
3199 }
3200 
3202 {
3203  if ( mStroke )
3204  {
3205  mStroke->stopRender( context.renderContext() );
3206  }
3207 }
3208 
3210 {
3211  QgsStringMap map;
3212  map.insert( QStringLiteral( "distance_x" ), QString::number( mDistanceX ) );
3213  map.insert( QStringLiteral( "distance_y" ), QString::number( mDistanceY ) );
3214  map.insert( QStringLiteral( "displacement_x" ), QString::number( mDisplacementX ) );
3215  map.insert( QStringLiteral( "displacement_y" ), QString::number( mDisplacementY ) );
3216  map.insert( QStringLiteral( "distance_x_unit" ), QgsUnitTypes::encodeUnit( mDistanceXUnit ) );
3217  map.insert( QStringLiteral( "distance_y_unit" ), QgsUnitTypes::encodeUnit( mDistanceYUnit ) );
3218  map.insert( QStringLiteral( "displacement_x_unit" ), QgsUnitTypes::encodeUnit( mDisplacementXUnit ) );
3219  map.insert( QStringLiteral( "displacement_y_unit" ), QgsUnitTypes::encodeUnit( mDisplacementYUnit ) );
3220  map.insert( QStringLiteral( "distance_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3221  map.insert( QStringLiteral( "distance_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3222  map.insert( QStringLiteral( "displacement_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3223  map.insert( QStringLiteral( "displacement_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3224  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
3225  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
3226  return map;
3227 }
3228 
3230 {
3232  if ( mMarkerSymbol )
3233  {
3234  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3235  }
3236  copyDataDefinedProperties( clonedLayer );
3237  copyPaintEffect( clonedLayer );
3238  return clonedLayer;
3239 }
3240 
3241 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3242 {
3243  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3244  {
3245  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
3246  if ( !props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ).isEmpty() )
3247  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ) );
3248  element.appendChild( symbolizerElem );
3249 
3250  // <Geometry>
3251  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QLatin1String( "" ) ) );
3252 
3253  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
3254  symbolizerElem.appendChild( fillElem );
3255 
3256  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
3257  fillElem.appendChild( graphicFillElem );
3258 
3259  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3262  QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
3263  QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "distance" ), dist );
3264  symbolizerElem.appendChild( distanceElem );
3265 
3266  QgsSymbolLayer *layer = mMarkerSymbol->symbolLayer( i );
3267  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
3268  if ( !markerLayer )
3269  {
3270  QString errorMsg = QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3271  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3272  }
3273  else
3274  {
3275  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3276  }
3277  }
3278 }
3279 
3281 {
3282  Q_UNUSED( element );
3283  return nullptr;
3284 }
3285 
3287 {
3288  if ( !symbol )
3289  {
3290  return false;
3291  }
3292 
3293  if ( symbol->type() == QgsSymbol::Marker )
3294  {
3295  QgsMarkerSymbol *markerSymbol = static_cast<QgsMarkerSymbol *>( symbol );
3296  delete mMarkerSymbol;
3297  mMarkerSymbol = markerSymbol;
3298  }
3299  return true;
3300 }
3301 
3303 {
3307  {
3308  return;
3309  }
3310 
3311  double distanceX = mDistanceX;
3313  {
3316  }
3317  double distanceY = mDistanceY;
3319  {
3322  }
3323  double displacementX = mDisplacementX;
3325  {
3328  }
3329  double displacementY = mDisplacementY;
3331  {
3334  }
3335  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3336 }
3337 
3339 {
3340  return 0;
3341 }
3342 
3344 {
3345  QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes( context );
3346 
3347  if ( mMarkerSymbol )
3348  attributes.unite( mMarkerSymbol->usedAttributes( context ) );
3349 
3350  return attributes;
3351 }
3352 
3354 {
3355  mColor = c;
3356  if ( mMarkerSymbol )
3357  mMarkerSymbol->setColor( c );
3358 }
3359 
3361 {
3362  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3363 }
3364 
3366 
3367 
3369 {
3370  setSubSymbol( new QgsMarkerSymbol() );
3371 }
3372 
3374 {
3376 
3377  if ( properties.contains( QStringLiteral( "point_on_surface" ) ) )
3378  sl->setPointOnSurface( properties[QStringLiteral( "point_on_surface" )].toInt() != 0 );
3379  if ( properties.contains( QStringLiteral( "point_on_all_parts" ) ) )
3380  sl->setPointOnAllParts( properties[QStringLiteral( "point_on_all_parts" )].toInt() != 0 );
3381 
3382  sl->restoreOldDataDefinedProperties( properties );
3383 
3384  return sl;
3385 }
3386 
3388 {
3389  return QStringLiteral( "CentroidFill" );
3390 }
3391 
3393 {
3394  mMarker->setColor( color );
3395  mColor = color;
3396 }
3397 
3399 {
3400  return mMarker ? mMarker->color() : mColor;
3401 }
3402 
3404 {
3405  mMarker->setOpacity( context.opacity() );
3406  mMarker->startRender( context.renderContext(), context.fields() );
3407 
3408  mCurrentFeatureId = -1;
3409  mBiggestPartIndex = 0;
3410 }
3411 
3413 {
3414  mMarker->stopRender( context.renderContext() );
3415 }
3416 
3417 void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3418 {
3419  Q_UNUSED( rings );
3420 
3421  if ( !mPointOnAllParts )
3422  {
3423  const QgsFeature *feature = context.feature();
3424  if ( feature )
3425  {
3426  if ( feature->id() != mCurrentFeatureId )
3427  {
3428  mCurrentFeatureId = feature->id();
3429  mBiggestPartIndex = 1;
3430 
3431  if ( context.geometryPartCount() > 1 )
3432  {
3433  const QgsGeometry geom = feature->geometry();
3434  const QgsGeometryCollection *geomCollection = static_cast<const QgsGeometryCollection *>( geom.constGet() );
3435 
3436  double area = 0;
3437  double areaBiggest = 0;
3438  for ( int i = 0; i < context.geometryPartCount(); ++i )
3439  {
3440  area = geomCollection->geometryN( i )->area();
3441  if ( area > areaBiggest )
3442  {
3443  areaBiggest = area;
3444  mBiggestPartIndex = i + 1;
3445  }
3446  }
3447  }
3448  }
3449  }
3450  }
3451 
3452  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3453  {
3454  QPointF centroid = mPointOnSurface ? QgsSymbolLayerUtils::polygonPointOnSurface( points ) : QgsSymbolLayerUtils::polygonCentroid( points );
3455  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3456  }
3457 }
3458 
3460 {
3461  QgsStringMap map;
3462  map[QStringLiteral( "point_on_surface" )] = QString::number( mPointOnSurface );
3463  map[QStringLiteral( "point_on_all_parts" )] = QString::number( mPointOnAllParts );
3464  return map;
3465 }
3466 
3468 {
3470  x->mAngle = mAngle;
3471  x->mColor = mColor;
3472  x->setSubSymbol( mMarker->clone() );
3473  x->setPointOnSurface( mPointOnSurface );
3474  x->setPointOnAllParts( mPointOnAllParts );
3476  copyPaintEffect( x );
3477  return x;
3478 }
3479 
3480 void QgsCentroidFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3481 {
3482  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3483  // used with PointSymbolizer, then the semantic is to use the centroid
3484  // of the geometry, or any similar representative point.
3485  mMarker->toSld( doc, element, props );
3486 }
3487 
3489 {
3490  QgsDebugMsg( "Entered." );
3491 
3493  if ( !l )
3494  return nullptr;
3495 
3496  QgsSymbolLayerList layers;
3497  layers.append( l );
3498  std::unique_ptr< QgsMarkerSymbol > marker( new QgsMarkerSymbol( layers ) );
3499 
3501  sl->setSubSymbol( marker.release() );
3502  return sl;
3503 }
3504 
3505 
3507 {
3508  return mMarker.get();
3509 }
3510 
3512 {
3513  if ( !symbol || symbol->type() != QgsSymbol::Marker )
3514  {
3515  delete symbol;
3516  return false;
3517  }
3518 
3519  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
3520  mColor = mMarker->color();
3521  return true;
3522 }
3523 
3525 {
3526  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
3527 
3528  if ( mMarker )
3529  attributes.unite( mMarker->usedAttributes( context ) );
3530 
3531  return attributes;
3532 }
3533 
3535 {
3536  if ( mMarker )
3537  {
3538  mMarker->setOutputUnit( unit );
3539  }
3540 }
3541 
3543 {
3544  if ( mMarker )
3545  {
3546  return mMarker->outputUnit();
3547  }
3548  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
3549 }
3550 
3552 {
3553  if ( mMarker )
3554  {
3555  mMarker->setMapUnitScale( scale );
3556  }
3557 }
3558 
3560 {
3561  if ( mMarker )
3562  {
3563  return mMarker->mapUnitScale();
3564  }
3565  return QgsMapUnitScale();
3566 }
3567 
3568 
3569 
3570 
3573  , mImageFilePath( imageFilePath )
3574 {
3575  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3576 }
3577 
3579 {
3581  double alpha = 1.0;
3582  QPointF offset;
3583  double angle = 0.0;
3584  double width = 0.0;
3585 
3586  QString imagePath;
3587  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3588  {
3589  imagePath = properties[QStringLiteral( "imageFile" )];
3590  }
3591  if ( properties.contains( QStringLiteral( "coordinate_mode" ) ) )
3592  {
3593  mode = static_cast< FillCoordinateMode >( properties[QStringLiteral( "coordinate_mode" )].toInt() );
3594  }
3595  if ( properties.contains( QStringLiteral( "alpha" ) ) )
3596  {
3597  alpha = properties[QStringLiteral( "alpha" )].toDouble();
3598  }
3599  if ( properties.contains( QStringLiteral( "offset" ) ) )
3600  {
3601  offset = QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] );
3602  }
3603  if ( properties.contains( QStringLiteral( "angle" ) ) )
3604  {
3605  angle = properties[QStringLiteral( "angle" )].toDouble();
3606  }
3607  if ( properties.contains( QStringLiteral( "width" ) ) )
3608  {
3609  width = properties[QStringLiteral( "width" )].toDouble();
3610  }
3611  QgsRasterFillSymbolLayer *symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
3612  symbolLayer->setCoordinateMode( mode );
3613  symbolLayer->setOpacity( alpha );
3614  symbolLayer->setOffset( offset );
3615  symbolLayer->setAngle( angle );
3616  symbolLayer->setWidth( width );
3617  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3618  {
3619  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
3620  }
3621  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3622  {
3623  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
3624  }
3625  if ( properties.contains( QStringLiteral( "width_unit" ) ) )
3626  {
3627  symbolLayer->setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "width_unit" )] ) );
3628  }
3629  if ( properties.contains( QStringLiteral( "width_map_unit_scale" ) ) )
3630  {
3631  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "width_map_unit_scale" )] ) );
3632  }
3633 
3634  symbolLayer->restoreOldDataDefinedProperties( properties );
3635 
3636  return symbolLayer;
3637 }
3638 
3640 {
3641  QgsStringMap::iterator it = properties.find( QStringLiteral( "imageFile" ) );
3642  if ( it != properties.end() )
3643  {
3644  if ( saving )
3645  it.value() = pathResolver.writePath( it.value() );
3646  else
3647  it.value() = pathResolver.readPath( it.value() );
3648  }
3649 }
3650 
3652 {
3653  Q_UNUSED( symbol );
3654  return true;
3655 }
3656 
3658 {
3659  return QStringLiteral( "RasterFill" );
3660 }
3661 
3662 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3663 {
3664  QPainter *p = context.renderContext().painter();
3665  if ( !p )
3666  {
3667  return;
3668  }
3669 
3670  QPointF offset;
3671  if ( !mOffset.isNull() )
3672  {
3675  p->translate( offset );
3676  }
3677  if ( mCoordinateMode == Feature )
3678  {
3679  QRectF boundingRect = points.boundingRect();
3680  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3681  boundingRect.top() - mBrush.transform().dy() ) );
3682  }
3683 
3684  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3685  if ( !mOffset.isNull() )
3686  {
3687  p->translate( -offset );
3688  }
3689 }
3690 
3692 {
3693  applyPattern( mBrush, mImageFilePath, mWidth, mOpacity, context );
3694 }
3695 
3697 {
3698  Q_UNUSED( context );
3699 }
3700 
3702 {
3703  QgsStringMap map;
3704  map[QStringLiteral( "imageFile" )] = mImageFilePath;
3705  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
3706  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3707  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3708  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3709  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3710  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3711  map[QStringLiteral( "width" )] = QString::number( mWidth );
3712  map[QStringLiteral( "width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
3713  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
3714  return map;
3715 }
3716 
3718 {
3721  sl->setOpacity( mOpacity );
3722  sl->setOffset( mOffset );
3723  sl->setOffsetUnit( mOffsetUnit );
3725  sl->setAngle( mAngle );
3726  sl->setWidth( mWidth );
3727  sl->setWidthUnit( mWidthUnit );
3730  copyPaintEffect( sl );
3731  return sl;
3732 }
3733 
3735 {
3736  return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
3737 }
3738 
3739 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
3740 {
3741  mImageFilePath = imagePath;
3742 }
3743 
3745 {
3746  mCoordinateMode = mode;
3747 }
3748 
3750 {
3751  mOpacity = opacity;
3752 }
3753 
3755 {
3756  if ( !dataDefinedProperties().hasActiveProperties() )
3757  return; // shortcut
3758 
3759  bool hasWidthExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
3760  bool hasFileExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFile );
3761  bool hasOpacityExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity );
3762  bool hasAngleExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
3763 
3764  if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
3765  {
3766  return; //no data defined settings
3767  }
3768 
3769  bool ok;
3770  if ( hasAngleExpression )
3771  {
3772  context.setOriginalValueVariable( mAngle );
3774  if ( ok )
3775  mNextAngle = nextAngle;
3776  }
3777 
3778  if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
3779  {
3780  return; //nothing further to do
3781  }
3782 
3783  double width = mWidth;
3784  if ( hasWidthExpression )
3785  {
3786  context.setOriginalValueVariable( mWidth );
3788  }
3789  double opacity = mOpacity;
3790  if ( hasOpacityExpression )
3791  {
3794  }
3795  QString file = mImageFilePath;
3796  if ( hasFileExpression )
3797  {
3800  }
3801  applyPattern( mBrush, file, width, opacity, context );
3802 }
3803 
3804 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolRenderContext &context )
3805 {
3806  QImage image( imageFilePath );
3807  if ( image.isNull() )
3808  {
3809  return;
3810  }
3811  if ( !image.hasAlphaChannel() )
3812  {
3813  image = image.convertToFormat( QImage::Format_ARGB32 );
3814  }
3815 
3816  double pixelWidth;
3817  if ( width > 0 )
3818  {
3820  }
3821  else
3822  {
3823  pixelWidth = image.width();
3824  }
3825 
3826  //reduce alpha of image
3827  if ( alpha < 1.0 )
3828  {
3829  QPainter p;
3830  p.begin( &image );
3831  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3832  QColor alphaColor( 0, 0, 0 );
3833  alphaColor.setAlphaF( alpha );
3834  p.fillRect( image.rect(), alphaColor );
3835  p.end();
3836  }
3837 
3838  //resize image if required
3839  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3840  {
3841  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3842  }
3843 
3844  brush.setTextureImage( image );
3845 }
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
const QgsMapUnitScale & intervalMapUnitScale() const
QgsMapUnitScale mapUnitScale() const override
QColor color2() const
Returns the color used for the endpoint of the shapeburst fill.
void setSvgStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
#define DEFAULT_SIMPLEFILL_BORDERCOLOR
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
QgsUnitTypes::RenderUnit mStrokeWidthUnit
QgsMapUnitScale mapUnitScale() const override
void setForceVectorOutput(bool force)
static QgsSymbolLayer * createFromSld(QDomElement &element)
QgsFeatureId id
Definition: qgsfeature.h:71
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
double interval() const
Returns the interval between individual markers.
void stopRender(QgsSymbolRenderContext &context) override
QgsSimpleFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, Qt::BrushStyle style=DEFAULT_SIMPLEFILL_STYLE, const QColor &strokeColor=DEFAULT_SIMPLEFILL_BORDERCOLOR, Qt::PenStyle strokeStyle=DEFAULT_SIMPLEFILL_BORDERSTYLE, double strokeWidth=DEFAULT_SIMPLEFILL_BORDERWIDTH, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEFILL_JOINSTYLE)
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
void setDistance(double d)
Sets the distance between lines in the fill pattern.
Gradient reference point 1 is centroid.
QgsSimpleFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
double mStrokeWidth
Stroke width.
double rendererScale() const
Returns the renderer map scale.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mDisplacementXUnit
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used for the gradient fill.
QString svgFilePath() const
void setPatternWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the width of the SVG images in the pattern.
QColor strokeColor() const override
Gets stroke color.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static Qt::BrushStyle decodeBrushStyle(const QString &str)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
FillCoordinateMode mCoordinateMode
QgsSVGFillSymbolLayer(const QString &svgFilePath, double width=20, double rotation=0.0)
Constructs SVG fill symbol layer with picture from given absolute path to a SVG file.
#define DEFAULT_SIMPLEFILL_JOINSTYLE
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Gradient reference point 1 x.
QByteArray getImageData(const QString &path) const
Gets image data.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:534
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the fill&#39;s offset.
QgsMapUnitScale mStrokeWidthMapUnitScale
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
QgsRasterFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void stopRender(QgsSymbolRenderContext &context) override
void setSvgFillColor(const QColor &c)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPatternWidthMapUnitScale(const QgsMapUnitScale &scale)
QString layerType() const override
Returns a string that represents this layer type.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void setColor(const QColor &c) override
The fill color.
QString imageFilePath() const
The path to the raster image used for the fill.
QPointF offset() const
Returns the offset for the shapeburst fill.
Use antialiasing while drawing.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QgsMapUnitScale mapUnitScale() const override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
GradientCoordinateMode coordinateMode() const
Coordinate mode for gradient. Controls how the gradient stops are positioned.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void startRender(QgsSymbolRenderContext &context) override
void stopRender(QgsSymbolRenderContext &context) override
QgsPointPatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setReferencePoint1IsCentroid(bool isCentroid)
Sets the starting point of the gradient to be the feature centroid.
Qt::PenJoinStyle penJoinStyle() const
GradientSpread gradientSpread() const
Gradient spread mode. Controls how the gradient behaves outside of the predefined stops...
QgsUnitTypes::RenderUnit mOffsetUnit
double angle() const
QgsMapUnitScale mapUnitScale() const override
Base class for polygon renderers generating texture images.
QgsMapUnitScale mPatternWidthMapUnitScale
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used to draw the shapeburst fill.
void startRender(QgsSymbolRenderContext &context) override
void setDisplacementYUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the vertical displacement between rows in the pattern.
void stopRender(QgsSymbolRenderContext &context) override
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
void setOffset(QPointF offset)
Sets the offset for the shapeburst fill.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
QgsUnitTypes::RenderUnit patternWidthUnit() const
Returns the units for the width of the SVG images in the pattern.
void setAngle(double angle)
void startRender(QgsSymbolRenderContext &context) override
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
Helper functions for various unit types.
Definition: qgsunittypes.h:37
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void setRendererScale(double scale)
Sets the renderer map scale.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
virtual QColor strokeColor() const
Gets stroke color.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1684
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
QgsUnitTypes::RenderUnit mSvgStrokeWidthUnit
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void setDistanceUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the maximum distance to shade inside of the shape from the polygon&#39;s boundary...
ShapeburstColorType colorType() const
Returns the color mode used for the shapeburst fill.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QgsMapUnitScale mapUnitScale() const override
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
void setDistanceUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the distance between lines in the fill pattern.
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void _renderPolygon(QPainter *p, const QPolygonF &points, const QList< QPolygonF > *rings, QgsSymbolRenderContext &context)
Default method to render polygon.
bool useWholeShape() const
Returns whether the shapeburst fill is set to cover the entire shape.
void setOffsetUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units for the fill&#39;s offset.
Flags flags() const
Returns combination of flags used for rendering.
static QgsSymbolLayer * createFromSld(QDomElement &element)
int symbolLayerCount() const
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:143
void setDistanceXMapUnitScale(const QgsMapUnitScale &scale)
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
void setDistanceMapUnitScale(const QgsMapUnitScale &scale)
#define DEFAULT_SIMPLEFILL_COLOR
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Line symbol.
Definition: qgssymbol.h:86
double mDistance
Distance (in mm or map units) between lines.
void startRender(QgsSymbolRenderContext &context) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
QColor color2() const
Color for endpoint of gradient, only used if the gradient color type is set to SimpleTwoColor.
QgsGradientFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
double mLineAngle
Vector line angle in degrees (0 = horizontal, counterclockwise)
void setReferencePoint2IsCentroid(bool isCentroid)
Sets the end point of the gradient to be the feature centroid.
QgsUnitTypes::RenderUnit mDistanceUnit
GradientColorType mGradientColorType
const QgsMapUnitScale & svgStrokeWidthMapUnitScale() const
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
QColor color() const override
The fill color.
void setPointOnAllParts(bool pointOnAllParts)
Sets whether a point is drawn for all parts or only on the biggest part of multi-part features...
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QString layerType() const override
Returns a string that represents this layer type.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QgsSymbolLayer * createFromSld(QDomElement &element)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s stroke width.
QColor color() const override
The fill color.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:396
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Gradient reference point 2 y.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setInterval(double interval)
Sets the interval between individual markers.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEFILL_STYLE
static QString encodeColor(const QColor &color)
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line pattern&#39;s offset.
void setCoordinateMode(FillCoordinateMode mode)
Set the coordinate mode for fill.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:251
void setOffset(QPointF offset)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
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...
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1475
QString layerType() const override
Returns a string that represents this layer type.
QgsUnitTypes::RenderUnit mOffsetUnit
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1050
QString mImageFilePath
Path to the image file.
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
void setIgnoreRings(bool ignoreRings)
Sets whether the shapeburst fill should ignore polygon rings when calculating the buffered shading...
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mOffsetUnit
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
QgsLinePatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
std::unique_ptr< QgsLineSymbol > mStroke
Custom stroke.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
A class for filling symbols with a repeated raster image.
QByteArray mSvgData
SVG data.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
#define DEFAULT_SIMPLEFILL_BORDERWIDTH
QColor dxfBrushColor(QgsSymbolRenderContext &context) const override
Gets brush/fill color.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setWidth(const double width)
Sets the width for scaling the image used in the fill.
double mOffset
Offset perpendicular to line direction.
static const bool SELECT_FILL_STYLE
Whether fill styles for selected features uses symbol layer style.
void setColor(const QColor &color) override
The fill color.
void setReferencePoint1(QPointF referencePoint)
Starting point of gradient fill, in the range [0,0] - [1,1].
QgsUnitTypes::RenderUnit mWidthUnit
Geometry collection.
void setSvgStrokeWidth(double w)
void setWidth(double width)
Definition: qgssymbol.cpp:1551
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:483
static QgsSymbolLayer * createFromSld(QDomElement &element)
double width() const
Definition: qgssymbol.cpp:1577
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the image&#39;s width.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
virtual QColor color() const
The fill color.
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void setDistanceYMapUnitScale(const QgsMapUnitScale &scale)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Filename, eg for svg files.
QColor selectionColor() const
QgsGradientFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, GradientColorType gradientColorType=SimpleTwoColor, GradientType gradientType=Linear, GradientCoordinateMode coordinateMode=Feature, GradientSpread gradientSpread=Pad)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void startRender(QgsSymbolRenderContext &context) override
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:53
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QgsUnitTypes::RenderUnit mOffsetUnit
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QColor color() const
Definition: qgssymbol.cpp:449
Shapeburst fill from edge distance.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsSymbol::SymbolType type() const
bool ignoreRings() const
Returns whether the shapeburst fill is set to ignore polygon interior rings.
virtual double area() const
Returns the area of the geometry.
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
virtual QString type() const =0
Returns a string representing the color ramp type.
QString layerType() const override
Returns a string that represents this layer type.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
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)
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
void stopRender(QgsSymbolRenderContext &context) override
double mapUnitsPerPixel() const
Returns current map units per pixel.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QColor fillColor() const override
Gets fill color.
QgsUnitTypes::RenderUnit svgStrokeWidthUnit() const
Returns the units for the stroke width.
QColor svgStrokeColor() const
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:453
void stopRender(QgsSymbolRenderContext &context) override
void setLineWidthMapUnitScale(const QgsMapUnitScale &scale)
QColor color() const override
The fill color.
QgsSymbolLayer * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
Definition: qgssymbol.cpp:332
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
void setDisplacementXMapUnitScale(const QgsMapUnitScale &scale)
QgsMapUnitScale mOffsetMapUnitScale
static Qt::PenStyle decodePenStyle(const QString &str)
void setOffset(QPointF offset)
Sets the offset for the fill.
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static void premultiplyColor(QColor &rgb, int alpha)
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value.
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
QString mSvgFilePath
Path to the svg file (or empty if constructed directly from data)
virtual double estimateMaxBleed(const QgsRenderContext &context) const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
bool selected() const
Definition: qgssymbol.h:492
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
double mLineWidth
Line width (in mm or map units)
QgsShapeburstFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setSvgStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
Gradient reference point 1 y.
void setOffset(QPointF offset)
Offset for gradient fill.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QgsMapUnitScale mapUnitScale() const override
double dxfAngle(QgsSymbolRenderContext &context) const override
Gets angle.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setDistanceXUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the horizontal distance between points in the pattern.
void setMapUnitScale(const QgsMapUnitScale &scale) override
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsExpressionContext & expressionContext()
Gets the expression context.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
QString layerType() const override
Returns a string that represents this layer type.
void startRender(QgsSymbolRenderContext &context) override
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
QString layerType() const override
Returns a string that represents this layer type.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
int geometryPartNum() const
Part number of current geometry.
Definition: qgssymbol.h:552
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
GradientCoordinateMode mCoordinateMode
GradientType gradientType() const
Type of gradient, e.g., linear or radial.
QgsRasterFillSymbolLayer(const QString &imageFilePath=QString())
void setMapUnitScale(const QgsMapUnitScale &scale) override
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsMapUnitScale mWidthMapUnitScale
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsColorRamp from a map of properties.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Marker symbol.
Definition: qgssymbol.h:85
A class for svg fill patterns.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Stroke style (eg solid, dashed)
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:643
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QgsUnitTypes::RenderUnit mLineWidthUnit
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the fill&#39;s offset.
QString layerType() const override
Returns a string that represents this layer type.
QgsCentroidFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Qt::BrushStyle dxfBrushStyle() const override
Gets brush/fill style.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:657
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
SymbolType type() const
Definition: qgssymbol.h:113
void setWidthUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units for the image&#39;s width.
~QgsShapeburstFillSymbolLayer() override
Struct for storing maximum and minimum scales for measurements in map units.
static QString encodeBrushStyle(Qt::BrushStyle style)
const QgsMapUnitScale & patternWidthMapUnitScale() const
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setSvgFilePath(const QString &svgPath)
virtual QgsStringMap properties() const =0
Returns a string map containing all the color ramp&#39;s properties.
QString ogrFeatureStyleWidth(double widthScaleFactor) const
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
Qt::PenStyle strokeStyle() const
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the width of the fill&#39;s stroke.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:509
#define DEFAULT_SIMPLEFILL_BORDERSTYLE
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
GradientColorType gradientColorType() const
Gradient color mode, controls how gradient color stops are created.
Qt::PenJoinStyle mPenJoinStyle
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setMapToPixel(const QgsMapToPixel &mtp)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setDisplacementXUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the horizontal displacement between rows in the pattern.
Secondary color (eg for gradient fills)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double maxDistance() const
Returns the maximum distance from the shape&#39;s boundary which is shaded.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void setDisplacementYMapUnitScale(const QgsMapUnitScale &scale)
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
double opacity() const
Returns the opacity for the raster image used in the fill.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QgsSymbolLayer * createLineLayerFromSld(QDomElement &element)
void setMapUnitScale(const QgsMapUnitScale &scale) override
static const bool SELECT_FILL_BORDER
Whether fill styles for selected features also highlight symbol stroke.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
void setColor(const QColor &c) override
The fill color.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
double svgStrokeWidth() const
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
QgsUnitTypes::RenderUnit mDistanceXUnit
int blurRadius() const
Returns the blur radius, which controls the amount of blurring applied to the fill.
#define INF
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the offset for the shapeburst fill.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
static QPointF polygonPointOnSurface(const QPolygonF &points)
Calculate a point within of a QPolygonF.
Shapeburst blur radius.
QgsUnitTypes::RenderUnit mDisplacementYUnit
void stopRender(QgsSymbolRenderContext &context) override
Resolves relative paths into absolute paths and vice versa.
void setLineWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line&#39;s width.
Draw map such that there are no problems between adjacent tiles.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void startRender(QgsSymbolRenderContext &context) override
void setSvgStrokeColor(const QColor &c)
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Units for gradient fill offset.
void addStopsToGradient(QGradient *gradient, double opacity=1)
Copy color ramp stops to a QGradient.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
double distance() const
Returns the distance between lines in the fill pattern.
void stopRender(QgsSymbolRenderContext &context) override
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Fill style (eg solid, dots)
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
void setDistanceYUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the vertical distance between points in the pattern.
virtual QColor fillColor() const
Gets fill color.
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
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.
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:418
double width() const
Returns the width used for scaling the image used in the fill.
QgsUnitTypes::RenderUnit intervalUnit() const
Returns the units for the interval between markers.
QgsSVGFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Gradient reference point 2 is centroid.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsShapeburstFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, ShapeburstColorType colorType=SimpleTwoColor, int blurRadius=0, bool useWholeShape=true, double maxDistance=5)
QgsUnitTypes::RenderUnit mPatternWidthUnit
QgsMapUnitScale mOffsetMapUnitScale
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void setReferencePoint2(QPointF referencePoint)
End point of gradient fill, in the range [0,0] - [1,1].
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Gradient reference point 2 x.
QgsPropertyCollection mDataDefinedProperties
QgsUnitTypes::RenderUnit mDistanceYUnit
void startRender(QgsSymbolRenderContext &context) override
int geometryPartCount() const
Part count of current geometry.
Definition: qgssymbol.h:540
void setDistanceMapUnitScale(const QgsMapUnitScale &scale)
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
QgsMarkerSymbol * clone() const override
Gets a deep copy of this symbol.
Definition: qgssymbol.cpp:1531
QgsMapUnitScale mSvgStrokeWidthMapUnitScale
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setPointOnSurface(bool pointOnSurface)
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QRectF mSvgViewBox
SVG view box (to keep the aspect ratio.
QImage * mSvgPattern
SVG pattern image.
QgsLineSymbol * clone() const override
Gets a deep copy of this symbol.
Definition: qgssymbol.cpp:1748
Horizontal distance between points.
QPointF offset() const
Returns the offset for the fill.
virtual QString layerType() const =0
Returns a string that represents this layer type.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &color)
Definition: qgssymbol.cpp:440
double mPatternWidth
Width of the pattern (in output units)
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Vertical distance between points.