QGIS API Documentation  3.4.3-Madeira (2f64a3c)
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  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< 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.release();
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 {
308  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( mColor, mBrushStyle, mStrokeColor, mStrokeStyle, mStrokeWidth, mPenJoinStyle );
309  sl->setOffset( mOffset );
310  sl->setOffsetUnit( mOffsetUnit );
311  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
312  sl->setStrokeWidthUnit( mStrokeWidthUnit );
313  sl->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
314  copyDataDefinedProperties( sl.get() );
315  copyPaintEffect( sl.get() );
316  return sl.release();
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" ), QString() ).isEmpty() )
326  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
327  element.appendChild( symbolizerElem );
328 
329  // <Geometry>
330  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
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  QColor color, strokeColor;
368  Qt::BrushStyle fillStyle;
369  Qt::PenStyle strokeStyle;
370  double strokeWidth;
371 
372  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
373  QgsSymbolLayerUtils::fillFromSld( fillElem, fillStyle, color );
374 
375  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
376  QgsSymbolLayerUtils::lineFromSld( strokeElem, strokeStyle, strokeColor, strokeWidth );
377 
378  QPointF offset;
380 
381  QString uom = element.attribute( QStringLiteral( "uom" ), QString() );
382  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
383  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
384  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
385 
386  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( color, fillStyle, strokeColor, strokeStyle, strokeWidth );
387  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
388  sl->setOffset( offset );
389  return sl.release();
390 }
391 
393 {
394  double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale );
395  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
396  return penBleed + offsetBleed;
397 }
398 
400 {
401  double width = mStrokeWidth;
403  {
406  }
408 }
409 
411 {
412  QColor c = mStrokeColor;
414  {
417  }
418  return c;
419 }
420 
422 {
423  double angle = mAngle;
425  {
426  context.setOriginalValueVariable( mAngle );
428  }
429  return angle;
430 }
431 
433 {
434  return mStrokeStyle;
435 }
436 
438 {
439  QColor c = mColor;
441  {
443  }
444  return c;
445 }
446 
448 {
449  return mBrushStyle;
450 }
451 
452 //QgsGradientFillSymbolLayer
453 
455  GradientColorType colorType, GradientType gradientType,
456  GradientCoordinateMode coordinateMode, GradientSpread spread )
457  : mGradientColorType( colorType )
458  , mGradientType( gradientType )
459  , mCoordinateMode( coordinateMode )
460  , mGradientSpread( spread )
461  , mReferencePoint1( QPointF( 0.5, 0 ) )
462  , mReferencePoint2( QPointF( 0.5, 1 ) )
463 {
464  mColor = color;
465  mColor2 = color2;
466 }
467 
469 {
470  delete mGradientRamp;
471 }
472 
474 {
475  //default to a two-color, linear gradient with feature mode and pad spreading
480  //default to gradient from the default fill color to white
481  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
482  QPointF referencePoint1 = QPointF( 0.5, 0 );
483  bool refPoint1IsCentroid = false;
484  QPointF referencePoint2 = QPointF( 0.5, 1 );
485  bool refPoint2IsCentroid = false;
486  double angle = 0;
487  QPointF offset;
488 
489  //update gradient properties from props
490  if ( props.contains( QStringLiteral( "type" ) ) )
491  type = static_cast< GradientType >( props[QStringLiteral( "type" )].toInt() );
492  if ( props.contains( QStringLiteral( "coordinate_mode" ) ) )
493  coordinateMode = static_cast< GradientCoordinateMode >( props[QStringLiteral( "coordinate_mode" )].toInt() );
494  if ( props.contains( QStringLiteral( "spread" ) ) )
495  gradientSpread = static_cast< GradientSpread >( props[QStringLiteral( "spread" )].toInt() );
496  if ( props.contains( QStringLiteral( "color_type" ) ) )
497  colorType = static_cast< GradientColorType >( props[QStringLiteral( "color_type" )].toInt() );
498  if ( props.contains( QStringLiteral( "gradient_color" ) ) )
499  {
500  //pre 2.5 projects used "gradient_color"
501  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color" )] );
502  }
503  else if ( props.contains( QStringLiteral( "color" ) ) )
504  {
505  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
506  }
507  if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
508  {
509  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
510  }
511 
512  if ( props.contains( QStringLiteral( "reference_point1" ) ) )
513  referencePoint1 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point1" )] );
514  if ( props.contains( QStringLiteral( "reference_point1_iscentroid" ) ) )
515  refPoint1IsCentroid = props[QStringLiteral( "reference_point1_iscentroid" )].toInt();
516  if ( props.contains( QStringLiteral( "reference_point2" ) ) )
517  referencePoint2 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point2" )] );
518  if ( props.contains( QStringLiteral( "reference_point2_iscentroid" ) ) )
519  refPoint2IsCentroid = props[QStringLiteral( "reference_point2_iscentroid" )].toInt();
520  if ( props.contains( QStringLiteral( "angle" ) ) )
521  angle = props[QStringLiteral( "angle" )].toDouble();
522 
523  if ( props.contains( QStringLiteral( "offset" ) ) )
524  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
525 
526  //attempt to create color ramp from props
527  QgsColorRamp *gradientRamp = nullptr;
528  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
529  {
530  gradientRamp = QgsCptCityColorRamp::create( props );
531  }
532  else
533  {
534  gradientRamp = QgsGradientColorRamp::create( props );
535  }
536 
537  //create a new gradient fill layer with desired properties
538  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( color, color2, colorType, type, coordinateMode, gradientSpread );
539  sl->setOffset( offset );
540  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
541  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
542  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
543  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
544  sl->setReferencePoint1( referencePoint1 );
545  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
546  sl->setReferencePoint2( referencePoint2 );
547  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
548  sl->setAngle( angle );
549  if ( gradientRamp )
550  sl->setColorRamp( gradientRamp );
551 
552  sl->restoreOldDataDefinedProperties( props );
553 
554  return sl.release();
555 }
556 
558 {
559  delete mGradientRamp;
560  mGradientRamp = ramp;
561 }
562 
564 {
565  return QStringLiteral( "GradientFill" );
566 }
567 
568 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, const QPolygonF &points )
569 {
570  if ( !dataDefinedProperties().hasActiveProperties() && !mReferencePoint1IsCentroid && !mReferencePoint2IsCentroid )
571  {
572  //shortcut
575  return;
576  }
577 
578  bool ok;
579 
580  //first gradient color
581  QColor color = mColor;
583  {
586  }
587 
588  //second gradient color
589  QColor color2 = mColor2;
591  {
594  }
595 
596  //gradient rotation angle
597  double angle = mAngle;
599  {
600  context.setOriginalValueVariable( mAngle );
602  }
603 
604  //gradient type
607  {
609  if ( ok )
610  {
611  if ( currentType == QObject::tr( "linear" ) )
612  {
613  gradientType = QgsGradientFillSymbolLayer::Linear;
614  }
615  else if ( currentType == QObject::tr( "radial" ) )
616  {
617  gradientType = QgsGradientFillSymbolLayer::Radial;
618  }
619  else if ( currentType == QObject::tr( "conical" ) )
620  {
622  }
623  }
624  }
625 
626  //coordinate mode
629  {
630  QString currentCoordMode = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCoordinateMode, context.renderContext().expressionContext(), QString(), &ok );
631  if ( ok )
632  {
633  if ( currentCoordMode == QObject::tr( "feature" ) )
634  {
635  coordinateMode = QgsGradientFillSymbolLayer::Feature;
636  }
637  else if ( currentCoordMode == QObject::tr( "viewport" ) )
638  {
639  coordinateMode = QgsGradientFillSymbolLayer::Viewport;
640  }
641  }
642  }
643 
644  //gradient spread
647  {
649  if ( ok )
650  {
651  if ( currentSpread == QObject::tr( "pad" ) )
652  {
654  }
655  else if ( currentSpread == QObject::tr( "repeat" ) )
656  {
658  }
659  else if ( currentSpread == QObject::tr( "reflect" ) )
660  {
662  }
663  }
664  }
665 
666  //reference point 1 x & y
667  double refPoint1X = mReferencePoint1.x();
669  {
670  context.setOriginalValueVariable( refPoint1X );
672  }
673  double refPoint1Y = mReferencePoint1.y();
675  {
676  context.setOriginalValueVariable( refPoint1Y );
678  }
679  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
681  {
682  context.setOriginalValueVariable( refPoint1IsCentroid );
684  }
685 
686  //reference point 2 x & y
687  double refPoint2X = mReferencePoint2.x();
689  {
690  context.setOriginalValueVariable( refPoint2X );
692  }
693  double refPoint2Y = mReferencePoint2.y();
695  {
696  context.setOriginalValueVariable( refPoint2Y );
698  }
699  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
701  {
702  context.setOriginalValueVariable( refPoint2IsCentroid );
704  }
705 
706  if ( refPoint1IsCentroid || refPoint2IsCentroid )
707  {
708  //either the gradient is starting or ending at a centroid, so calculate it
709  QPointF centroid = QgsSymbolLayerUtils::polygonCentroid( points );
710  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
711  QRectF bbox = points.boundingRect();
712  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
713  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
714 
715  if ( refPoint1IsCentroid )
716  {
717  refPoint1X = centroidX;
718  refPoint1Y = centroidY;
719  }
720  if ( refPoint2IsCentroid )
721  {
722  refPoint2X = centroidX;
723  refPoint2Y = centroidY;
724  }
725  }
726 
727  //update gradient with data defined values
728  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
729  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
730 }
731 
732 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint, double angle )
733 {
734  //rotate a reference point by a specified angle around the point (0.5, 0.5)
735 
736  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
737  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
738  //rotate this line by the current rotation angle
739  refLine.setAngle( refLine.angle() + angle );
740  //get new end point of line
741  QPointF rotatedReferencePoint = refLine.p2();
742  //make sure coords of new end point is within [0, 1]
743  if ( rotatedReferencePoint.x() > 1 )
744  rotatedReferencePoint.setX( 1 );
745  if ( rotatedReferencePoint.x() < 0 )
746  rotatedReferencePoint.setX( 0 );
747  if ( rotatedReferencePoint.y() > 1 )
748  rotatedReferencePoint.setY( 1 );
749  if ( rotatedReferencePoint.y() < 0 )
750  rotatedReferencePoint.setY( 0 );
751 
752  return rotatedReferencePoint;
753 }
754 
755 void QgsGradientFillSymbolLayer::applyGradient( const QgsSymbolRenderContext &context, QBrush &brush,
756  const QColor &color, const QColor &color2, GradientColorType gradientColorType,
757  QgsColorRamp *gradientRamp, GradientType gradientType,
759  QPointF referencePoint1, QPointF referencePoint2, const double angle )
760 {
761  //update alpha of gradient colors
762  QColor fillColor = color;
763  fillColor.setAlphaF( context.opacity() * fillColor.alphaF() );
764  QColor fillColor2 = color2;
765  fillColor2.setAlphaF( context.opacity() * fillColor2.alphaF() );
766 
767  //rotate reference points
768  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
769  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
770 
771  //create a QGradient with the desired properties
772  QGradient gradient;
773  switch ( gradientType )
774  {
776  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
777  break;
779  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
780  break;
782  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
783  break;
784  }
785  switch ( coordinateMode )
786  {
788  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
789  break;
791  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
792  break;
793  }
794  switch ( gradientSpread )
795  {
797  gradient.setSpread( QGradient::PadSpread );
798  break;
800  gradient.setSpread( QGradient::ReflectSpread );
801  break;
803  gradient.setSpread( QGradient::RepeatSpread );
804  break;
805  }
806 
807  //add stops to gradient
809  ( gradientRamp->type() == QLatin1String( "gradient" ) || gradientRamp->type() == QLatin1String( "cpt-city" ) ) )
810  {
811  //color ramp gradient
812  QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( gradientRamp );
813  gradRamp->addStopsToGradient( &gradient, context.opacity() );
814  }
815  else
816  {
817  //two color gradient
818  gradient.setColorAt( 0.0, fillColor );
819  gradient.setColorAt( 1.0, fillColor2 );
820  }
821 
822  //update QBrush use gradient
823  brush = QBrush( gradient );
824 }
825 
827 {
828  QColor selColor = context.renderContext().selectionColor();
829  if ( ! SELECTION_IS_OPAQUE )
830  selColor.setAlphaF( context.opacity() );
831  mSelBrush = QBrush( selColor );
832 }
833 
835 {
836  Q_UNUSED( context );
837 }
838 
839 void QgsGradientFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
840 {
841  QPainter *p = context.renderContext().painter();
842  if ( !p )
843  {
844  return;
845  }
846 
847  applyDataDefinedSymbology( context, points );
848 
849  p->setBrush( context.selected() ? mSelBrush : mBrush );
850  p->setPen( Qt::NoPen );
851 
852  QPointF offset;
853  if ( !mOffset.isNull() )
854  {
857  p->translate( offset );
858  }
859 
860  _renderPolygon( p, points, rings, context );
861 
862  if ( !mOffset.isNull() )
863  {
864  p->translate( -offset );
865  }
866 }
867 
869 {
870  QgsStringMap map;
871  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
872  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
873  map[QStringLiteral( "color_type" )] = QString::number( mGradientColorType );
874  map[QStringLiteral( "type" )] = QString::number( mGradientType );
875  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
876  map[QStringLiteral( "spread" )] = QString::number( mGradientSpread );
877  map[QStringLiteral( "reference_point1" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint1 );
878  map[QStringLiteral( "reference_point1_iscentroid" )] = QString::number( mReferencePoint1IsCentroid );
879  map[QStringLiteral( "reference_point2" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint2 );
880  map[QStringLiteral( "reference_point2_iscentroid" )] = QString::number( mReferencePoint2IsCentroid );
881  map[QStringLiteral( "angle" )] = QString::number( mAngle );
882  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
883  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
884  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
885  if ( mGradientRamp )
886  {
887  map.unite( mGradientRamp->properties() );
888  }
889  return map;
890 }
891 
893 {
894  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( mColor, mColor2, mGradientColorType, mGradientType, mCoordinateMode, mGradientSpread );
895  if ( mGradientRamp )
896  sl->setColorRamp( mGradientRamp->clone() );
897  sl->setReferencePoint1( mReferencePoint1 );
898  sl->setReferencePoint1IsCentroid( mReferencePoint1IsCentroid );
899  sl->setReferencePoint2( mReferencePoint2 );
900  sl->setReferencePoint2IsCentroid( mReferencePoint2IsCentroid );
901  sl->setAngle( mAngle );
902  sl->setOffset( mOffset );
903  sl->setOffsetUnit( mOffsetUnit );
904  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
905  copyDataDefinedProperties( sl.get() );
906  copyPaintEffect( sl.get() );
907  return sl.release();
908 }
909 
911 {
912  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
913  return offsetBleed;
914 }
915 
917 {
918  mOffsetUnit = unit;
919 }
920 
922 {
923  return mOffsetUnit;
924 }
925 
927 {
928  mOffsetMapUnitScale = scale;
929 }
930 
932 {
933  return mOffsetMapUnitScale;
934 }
935 
936 //QgsShapeburstFillSymbolLayer
937 
939  int blurRadius, bool useWholeShape, double maxDistance )
940  : mBlurRadius( blurRadius )
941  , mUseWholeShape( useWholeShape )
942  , mMaxDistance( maxDistance )
943  , mColorType( colorType )
944  , mColor2( color2 )
945 {
946  mColor = color;
947 }
948 
950 
952 {
953  //default to a two-color gradient
955  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
956  int blurRadius = 0;
957  bool useWholeShape = true;
958  double maxDistance = 5;
959  QPointF offset;
960 
961  //update fill properties from props
962  if ( props.contains( QStringLiteral( "color_type" ) ) )
963  {
964  colorType = static_cast< ShapeburstColorType >( props[QStringLiteral( "color_type" )].toInt() );
965  }
966  if ( props.contains( QStringLiteral( "shapeburst_color" ) ) )
967  {
968  //pre 2.5 projects used "shapeburst_color"
969  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color" )] );
970  }
971  else if ( props.contains( QStringLiteral( "color" ) ) )
972  {
973  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
974  }
975 
976  if ( props.contains( QStringLiteral( "shapeburst_color2" ) ) )
977  {
978  //pre 2.5 projects used "shapeburst_color2"
979  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color2" )] );
980  }
981  else if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
982  {
983  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
984  }
985  if ( props.contains( QStringLiteral( "blur_radius" ) ) )
986  {
987  blurRadius = props[QStringLiteral( "blur_radius" )].toInt();
988  }
989  if ( props.contains( QStringLiteral( "use_whole_shape" ) ) )
990  {
991  useWholeShape = props[QStringLiteral( "use_whole_shape" )].toInt();
992  }
993  if ( props.contains( QStringLiteral( "max_distance" ) ) )
994  {
995  maxDistance = props[QStringLiteral( "max_distance" )].toDouble();
996  }
997  if ( props.contains( QStringLiteral( "offset" ) ) )
998  {
999  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
1000  }
1001 
1002  //attempt to create color ramp from props
1003  QgsColorRamp *gradientRamp = nullptr;
1004  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
1005  {
1006  gradientRamp = QgsCptCityColorRamp::create( props );
1007  }
1008  else
1009  {
1010  gradientRamp = QgsGradientColorRamp::create( props );
1011  }
1012 
1013  //create a new shapeburst fill layer with desired properties
1014  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1015  sl->setOffset( offset );
1016  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1017  {
1018  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1019  }
1020  if ( props.contains( QStringLiteral( "distance_unit" ) ) )
1021  {
1022  sl->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "distance_unit" )] ) );
1023  }
1024  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1025  {
1026  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1027  }
1028  if ( props.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
1029  {
1030  sl->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "distance_map_unit_scale" )] ) );
1031  }
1032  if ( props.contains( QStringLiteral( "ignore_rings" ) ) )
1033  {
1034  sl->setIgnoreRings( props[QStringLiteral( "ignore_rings" )].toInt() );
1035  }
1036  if ( gradientRamp )
1037  {
1038  sl->setColorRamp( gradientRamp );
1039  }
1040 
1041  sl->restoreOldDataDefinedProperties( props );
1042 
1043  return sl.release();
1044 }
1045 
1047 {
1048  return QStringLiteral( "ShapeburstFill" );
1049 }
1050 
1052 {
1053  if ( mGradientRamp.get() == ramp )
1054  return;
1055 
1056  mGradientRamp.reset( ramp );
1057 }
1058 
1059 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QColor &color, QColor &color2, int &blurRadius, bool &useWholeShape,
1060  double &maxDistance, bool &ignoreRings )
1061 {
1062  //first gradient color
1063  color = mColor;
1065  {
1068  }
1069 
1070  //second gradient color
1071  color2 = mColor2;
1073  {
1076  }
1077 
1078  //blur radius
1079  blurRadius = mBlurRadius;
1081  {
1082  context.setOriginalValueVariable( mBlurRadius );
1084  }
1085 
1086  //use whole shape
1087  useWholeShape = mUseWholeShape;
1089  {
1090  context.setOriginalValueVariable( mUseWholeShape );
1092  }
1093 
1094  //max distance
1095  maxDistance = mMaxDistance;
1097  {
1098  context.setOriginalValueVariable( mMaxDistance );
1100  }
1101 
1102  //ignore rings
1103  ignoreRings = mIgnoreRings;
1105  {
1106  context.setOriginalValueVariable( mIgnoreRings );
1108  }
1109 
1110 }
1111 
1113 {
1114  //TODO - check this
1115  QColor selColor = context.renderContext().selectionColor();
1116  if ( ! SELECTION_IS_OPAQUE )
1117  selColor.setAlphaF( context.opacity() );
1118  mSelBrush = QBrush( selColor );
1119 }
1120 
1122 {
1123  Q_UNUSED( context );
1124 }
1125 
1126 void QgsShapeburstFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1127 {
1128  QPainter *p = context.renderContext().painter();
1129  if ( !p )
1130  {
1131  return;
1132  }
1133 
1134  if ( context.selected() )
1135  {
1136  //feature is selected, draw using selection style
1137  p->setBrush( mSelBrush );
1138  QPointF offset;
1139  if ( !mOffset.isNull() )
1140  {
1141  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1142  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1143  p->translate( offset );
1144  }
1145  _renderPolygon( p, points, rings, context );
1146  if ( !mOffset.isNull() )
1147  {
1148  p->translate( -offset );
1149  }
1150  return;
1151  }
1152 
1153  QColor color1, color2;
1154  int blurRadius;
1155  bool useWholeShape;
1156  double maxDistance;
1157  bool ignoreRings;
1158  //calculate data defined symbology
1159  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1160 
1161  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1162  int outputPixelMaxDist = 0;
1163  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1164  {
1165  //convert max distance to pixels
1166  outputPixelMaxDist = static_cast< int >( std::round( context.renderContext().convertToPainterUnits( maxDistance, mDistanceUnit, mDistanceMapUnitScale ) ) );
1167  }
1168 
1169  //if we are using the two color mode, create a gradient ramp
1170  std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1172  {
1173  twoColorGradientRamp = qgis::make_unique< QgsGradientColorRamp >( color1, color2 );
1174  }
1175 
1176  //no stroke for shapeburst fills
1177  p->setPen( QPen( Qt::NoPen ) );
1178 
1179  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1180  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1181  //create a QImage to draw shapeburst in
1182  int pointsWidth = static_cast< int >( std::round( points.boundingRect().width() ) );
1183  int pointsHeight = static_cast< int >( std::round( points.boundingRect().height() ) );
1184  int imWidth = pointsWidth + ( sideBuffer * 2 );
1185  int imHeight = pointsHeight + ( sideBuffer * 2 );
1186  std::unique_ptr< QImage > fillImage = qgis::make_unique< QImage >( imWidth,
1187  imHeight, QImage::Format_ARGB32_Premultiplied );
1188  if ( fillImage->isNull() )
1189  {
1190  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1191  return;
1192  }
1193 
1194  //also create an image to store the alpha channel
1195  std::unique_ptr< QImage > alphaImage = qgis::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1196  if ( alphaImage->isNull() )
1197  {
1198  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1199  return;
1200  }
1201 
1202  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1203  //polygon boundary. Since we don't care about pixels which fall outside the polygon, we start with a black image and then draw over it the
1204  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1205  fillImage->fill( Qt::black );
1206 
1207  //initially fill the alpha channel image with a transparent color
1208  alphaImage->fill( Qt::transparent );
1209 
1210  //now, draw the polygon in the alpha channel image
1211  QPainter imgPainter;
1212  imgPainter.begin( alphaImage.get() );
1213  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1214  imgPainter.setBrush( QBrush( Qt::white ) );
1215  imgPainter.setPen( QPen( Qt::black ) );
1216  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1217  _renderPolygon( &imgPainter, points, rings, context );
1218  imgPainter.end();
1219 
1220  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1221  //(this avoids calling _renderPolygon twice, since that can be slow)
1222  imgPainter.begin( fillImage.get() );
1223  if ( !ignoreRings )
1224  {
1225  imgPainter.drawImage( 0, 0, *alphaImage );
1226  }
1227  else
1228  {
1229  //using ignore rings mode, so the alpha image can't be used
1230  //directly as the alpha channel contains polygon rings and we need
1231  //to draw now without any rings
1232  imgPainter.setBrush( QBrush( Qt::white ) );
1233  imgPainter.setPen( QPen( Qt::black ) );
1234  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1235  _renderPolygon( &imgPainter, points, nullptr, context );
1236  }
1237  imgPainter.end();
1238 
1239  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1240  double *dtArray = distanceTransform( fillImage.get() );
1241 
1242  //copy distance transform values back to QImage, shading by appropriate color ramp
1243  dtArrayToQImage( dtArray, fillImage.get(), mColorType == QgsShapeburstFillSymbolLayer::SimpleTwoColor ? twoColorGradientRamp.get() : mGradientRamp.get(),
1244  context.opacity(), useWholeShape, outputPixelMaxDist );
1245 
1246  //clean up some variables
1247  delete [] dtArray;
1248 
1249  //apply blur if desired
1250  if ( blurRadius > 0 )
1251  {
1252  QgsSymbolLayerUtils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1253  }
1254 
1255  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1256  imgPainter.begin( fillImage.get() );
1257  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1258  imgPainter.drawImage( 0, 0, *alphaImage );
1259  imgPainter.end();
1260  //we're finished with the alpha channel image now
1261  alphaImage.reset();
1262 
1263  //draw shapeburst image in correct place in the destination painter
1264 
1265  p->save();
1266  QPointF offset;
1267  if ( !mOffset.isNull() )
1268  {
1269  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1270  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1271  p->translate( offset );
1272  }
1273 
1274  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1275 
1276  if ( !mOffset.isNull() )
1277  {
1278  p->translate( -offset );
1279  }
1280  p->restore();
1281 
1282 }
1283 
1284 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1285 
1286 /* distance transform of a 1d function using squared distance */
1287 void QgsShapeburstFillSymbolLayer::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1288 {
1289  int k = 0;
1290  v[0] = 0;
1291  z[0] = -INF;
1292  z[1] = + INF;
1293  for ( int q = 1; q <= n - 1; q++ )
1294  {
1295  double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1296  while ( s <= z[k] )
1297  {
1298  k--;
1299  s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1300  }
1301  k++;
1302  v[k] = q;
1303  z[k] = s;
1304  z[k + 1] = + INF;
1305  }
1306 
1307  k = 0;
1308  for ( int q = 0; q <= n - 1; q++ )
1309  {
1310  while ( z[k + 1] < q )
1311  k++;
1312  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1313  }
1314 }
1315 
1316 /* distance transform of 2d function using squared distance */
1317 void QgsShapeburstFillSymbolLayer::distanceTransform2d( double *im, int width, int height )
1318 {
1319  int maxDimension = std::max( width, height );
1320  double *f = new double[ maxDimension ];
1321  int *v = new int[ maxDimension ];
1322  double *z = new double[ maxDimension + 1 ];
1323  double *d = new double[ maxDimension ];
1324 
1325  // transform along columns
1326  for ( int x = 0; x < width; x++ )
1327  {
1328  for ( int y = 0; y < height; y++ )
1329  {
1330  f[y] = im[ x + y * width ];
1331  }
1332  distanceTransform1d( f, height, v, z, d );
1333  for ( int y = 0; y < height; y++ )
1334  {
1335  im[ x + y * width ] = d[y];
1336  }
1337  }
1338 
1339  // transform along rows
1340  for ( int y = 0; y < height; y++ )
1341  {
1342  for ( int x = 0; x < width; x++ )
1343  {
1344  f[x] = im[ x + y * width ];
1345  }
1346  distanceTransform1d( f, width, v, z, d );
1347  for ( int x = 0; x < width; x++ )
1348  {
1349  im[ x + y * width ] = d[x];
1350  }
1351  }
1352 
1353  delete [] d;
1354  delete [] f;
1355  delete [] v;
1356  delete [] z;
1357 }
1358 
1359 /* distance transform of a binary QImage */
1360 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im )
1361 {
1362  int width = im->width();
1363  int height = im->height();
1364 
1365  double *dtArray = new double[width * height];
1366 
1367  //load qImage to array
1368  QRgb tmpRgb;
1369  int idx = 0;
1370  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1371  {
1372  const QRgb *scanLine = reinterpret_cast< const QRgb * >( im->constScanLine( heightIndex ) );
1373  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1374  {
1375  tmpRgb = scanLine[widthIndex];
1376  if ( qRed( tmpRgb ) == 0 )
1377  {
1378  //black pixel, so zero distance
1379  dtArray[ idx ] = 0;
1380  }
1381  else
1382  {
1383  //white pixel, so initially set distance as infinite
1384  dtArray[ idx ] = INF;
1385  }
1386  idx++;
1387  }
1388  }
1389 
1390  //calculate squared distance transform
1391  distanceTransform2d( dtArray, width, height );
1392 
1393  return dtArray;
1394 }
1395 
1396 void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, QgsColorRamp *ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1397 {
1398  int width = im->width();
1399  int height = im->height();
1400 
1401  //find maximum distance value
1402  double maxDistanceValue;
1403 
1404  if ( useWholeShape )
1405  {
1406  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1407  double dtMaxValue = array[0];
1408  for ( int i = 1; i < ( width * height ); ++i )
1409  {
1410  if ( array[i] > dtMaxValue )
1411  {
1412  dtMaxValue = array[i];
1413  }
1414  }
1415 
1416  //values in distance transform are squared
1417  maxDistanceValue = std::sqrt( dtMaxValue );
1418  }
1419  else
1420  {
1421  //use max distance set in symbol properties
1422  maxDistanceValue = maxPixelDistance;
1423  }
1424 
1425  //update the pixels in the provided QImage
1426  int idx = 0;
1427  double squaredVal = 0;
1428  double pixVal = 0;
1429  QColor pixColor;
1430  bool layerHasAlpha = layerAlpha < 1.0;
1431 
1432  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1433  {
1434  QRgb *scanLine = reinterpret_cast< QRgb * >( im->scanLine( heightIndex ) );
1435  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1436  {
1437  //result of distance transform
1438  squaredVal = array[idx];
1439 
1440  //scale result to fit in the range [0, 1]
1441  if ( maxDistanceValue > 0 )
1442  {
1443  pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1444  }
1445  else
1446  {
1447  pixVal = 1.0;
1448  }
1449 
1450  //convert value to color from ramp
1451  pixColor = ramp->color( pixVal );
1452 
1453  int pixAlpha = pixColor.alpha();
1454  if ( ( layerHasAlpha ) || ( pixAlpha != 255 ) )
1455  {
1456  //apply layer's transparency to alpha value
1457  double alpha = pixAlpha * layerAlpha;
1458  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1459  QgsSymbolLayerUtils::premultiplyColor( pixColor, alpha );
1460  }
1461 
1462  scanLine[widthIndex] = pixColor.rgba();
1463  idx++;
1464  }
1465  }
1466 }
1467 
1469 {
1470  QgsStringMap map;
1471  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1472  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
1473  map[QStringLiteral( "color_type" )] = QString::number( mColorType );
1474  map[QStringLiteral( "blur_radius" )] = QString::number( mBlurRadius );
1475  map[QStringLiteral( "use_whole_shape" )] = QString::number( mUseWholeShape );
1476  map[QStringLiteral( "max_distance" )] = QString::number( mMaxDistance );
1477  map[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
1478  map[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
1479  map[QStringLiteral( "ignore_rings" )] = QString::number( mIgnoreRings );
1480  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1481  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1482  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1483  if ( mGradientRamp )
1484  {
1485  map.unite( mGradientRamp->properties() );
1486  }
1487 
1488  return map;
1489 }
1490 
1492 {
1493  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1494  if ( mGradientRamp )
1495  {
1496  sl->setColorRamp( mGradientRamp->clone() );
1497  }
1498  sl->setDistanceUnit( mDistanceUnit );
1499  sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1500  sl->setIgnoreRings( mIgnoreRings );
1501  sl->setOffset( mOffset );
1502  sl->setOffsetUnit( mOffsetUnit );
1503  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1504  copyDataDefinedProperties( sl.get() );
1505  copyPaintEffect( sl.get() );
1506  return sl.release();
1507 }
1508 
1510 {
1511  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1512  return offsetBleed;
1513 }
1514 
1516 {
1517  mDistanceUnit = unit;
1518  mOffsetUnit = unit;
1519 }
1520 
1522 {
1523  if ( mDistanceUnit == mOffsetUnit )
1524  {
1525  return mDistanceUnit;
1526  }
1528 }
1529 
1531 {
1532  mDistanceMapUnitScale = scale;
1533  mOffsetMapUnitScale = scale;
1534 }
1535 
1537 {
1538  if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1539  {
1540  return mDistanceMapUnitScale;
1541  }
1542  return QgsMapUnitScale();
1543 }
1544 
1545 
1546 //QgsImageFillSymbolLayer
1547 
1549 {
1550  setSubSymbol( new QgsLineSymbol() );
1551 }
1552 
1553 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1554 {
1555  QPainter *p = context.renderContext().painter();
1556  if ( !p )
1557  {
1558  return;
1559  }
1560 
1561  mNextAngle = mAngle;
1562  applyDataDefinedSettings( context );
1563 
1564  p->setPen( QPen( Qt::NoPen ) );
1565 
1566  QTransform bkTransform = mBrush.transform();
1568  {
1569  //transform brush to upper left corner of geometry bbox
1570  QPointF leftCorner = points.boundingRect().topLeft();
1571  QTransform t = mBrush.transform();
1572  t.translate( leftCorner.x(), leftCorner.y() );
1573  mBrush.setTransform( t );
1574  }
1575 
1576  if ( context.selected() )
1577  {
1578  QColor selColor = context.renderContext().selectionColor();
1579  // Alister - this doesn't seem to work here
1580  //if ( ! selectionIsOpaque )
1581  // selColor.setAlphaF( context.alpha() );
1582  p->setBrush( QBrush( selColor ) );
1583  _renderPolygon( p, points, rings, context );
1584  }
1585 
1586  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1587  {
1588  QTransform t = mBrush.transform();
1589  t.rotate( mNextAngle );
1590  mBrush.setTransform( t );
1591  }
1592  p->setBrush( mBrush );
1593  _renderPolygon( p, points, rings, context );
1594  if ( mStroke )
1595  {
1596  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1597  if ( rings )
1598  {
1599  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1600  for ( ; ringIt != rings->constEnd(); ++ringIt )
1601  {
1602  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1603  }
1604  }
1605  }
1606 
1607  mBrush.setTransform( bkTransform );
1608 }
1609 
1611 {
1612  if ( !symbol ) //unset current stroke
1613  {
1614  mStroke.reset( nullptr );
1615  return true;
1616  }
1617 
1618  if ( symbol->type() != QgsSymbol::Line )
1619  {
1620  delete symbol;
1621  return false;
1622  }
1623 
1624  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
1625  if ( lineSymbol )
1626  {
1627  mStroke.reset( lineSymbol );
1628  return true;
1629  }
1630 
1631  delete symbol;
1632  return false;
1633 }
1634 
1636 {
1637  mStrokeWidthUnit = unit;
1638 }
1639 
1641 {
1642  return mStrokeWidthUnit;
1643 }
1644 
1646 {
1647  mStrokeWidthMapUnitScale = scale;
1648 }
1649 
1651 {
1652  return mStrokeWidthMapUnitScale;
1653 }
1654 
1656 {
1657  if ( mStroke && mStroke->symbolLayer( 0 ) )
1658  {
1659  double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1660  return subLayerBleed;
1661  }
1662  return 0;
1663 }
1664 
1666 {
1667  double width = mStrokeWidth;
1669  {
1670  context.setOriginalValueVariable( mStrokeWidth );
1672  }
1673  return width * e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1674 }
1675 
1677 {
1678  Q_UNUSED( context );
1679  if ( !mStroke )
1680  {
1681  return QColor( Qt::black );
1682  }
1683  return mStroke->color();
1684 }
1685 
1687 {
1688  return Qt::SolidLine;
1689 #if 0
1690  if ( !mStroke )
1691  {
1692  return Qt::SolidLine;
1693  }
1694  else
1695  {
1696  return mStroke->dxfPenStyle();
1697  }
1698 #endif //0
1699 }
1700 
1701 QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1702 {
1703  QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
1704  if ( mStroke )
1705  attr.unite( mStroke->usedAttributes( context ) );
1706  return attr;
1707 }
1708 
1709 
1710 //QgsSVGFillSymbolLayer
1711 
1712 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString &svgFilePath, double width, double angle )
1714  , mPatternWidth( width )
1715 {
1716  setSvgFilePath( svgFilePath );
1717  mStrokeWidth = 0.3;
1718  mAngle = angle;
1719  mColor = QColor( 255, 255, 255 );
1720  setDefaultSvgParams();
1721 }
1722 
1723 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double width, double angle )
1725  , mPatternWidth( width )
1726  , mSvgData( svgData )
1727 {
1728  storeViewBox();
1729  mStrokeWidth = 0.3;
1730  mAngle = angle;
1731  mColor = QColor( 255, 255, 255 );
1732  setSubSymbol( new QgsLineSymbol() );
1733  setDefaultSvgParams();
1734 }
1735 
1737 {
1739  mPatternWidthUnit = unit;
1740  mSvgStrokeWidthUnit = unit;
1741  mStrokeWidthUnit = unit;
1742  mStroke->setOutputUnit( unit );
1743 }
1744 
1746 {
1748  if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit || mStrokeWidthUnit != unit )
1749  {
1751  }
1752  return unit;
1753 }
1754 
1756 {
1758  mPatternWidthMapUnitScale = scale;
1759  mSvgStrokeWidthMapUnitScale = scale;
1760  mStrokeWidthMapUnitScale = scale;
1761 }
1762 
1764 {
1765  if ( QgsImageFillSymbolLayer::mapUnitScale() == mPatternWidthMapUnitScale &&
1766  mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1767  mSvgStrokeWidthMapUnitScale == mStrokeWidthMapUnitScale )
1768  {
1769  return mPatternWidthMapUnitScale;
1770  }
1771  return QgsMapUnitScale();
1772 }
1773 
1774 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString &svgPath )
1775 {
1776  mSvgData = QgsApplication::svgCache()->getImageData( svgPath );
1777  storeViewBox();
1778 
1779  mSvgFilePath = svgPath;
1780  setDefaultSvgParams();
1781 }
1782 
1784 {
1785  QByteArray data;
1786  double width = 20;
1787  QString svgFilePath;
1788  double angle = 0.0;
1789 
1790  if ( properties.contains( QStringLiteral( "width" ) ) )
1791  {
1792  width = properties[QStringLiteral( "width" )].toDouble();
1793  }
1794  if ( properties.contains( QStringLiteral( "svgFile" ) ) )
1795  {
1796  svgFilePath = properties[QStringLiteral( "svgFile" )];
1797  }
1798  if ( properties.contains( QStringLiteral( "angle" ) ) )
1799  {
1800  angle = properties[QStringLiteral( "angle" )].toDouble();
1801  }
1802 
1803  std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1804  if ( !svgFilePath.isEmpty() )
1805  {
1806  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( svgFilePath, width, angle );
1807  }
1808  else
1809  {
1810  if ( properties.contains( QStringLiteral( "data" ) ) )
1811  {
1812  data = QByteArray::fromHex( properties[QStringLiteral( "data" )].toLocal8Bit() );
1813  }
1814  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( data, width, angle );
1815  }
1816 
1817  //svg parameters
1818  if ( properties.contains( QStringLiteral( "svgFillColor" ) ) )
1819  {
1820  //pre 2.5 projects used "svgFillColor"
1821  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgFillColor" )] ) );
1822  }
1823  else if ( properties.contains( QStringLiteral( "color" ) ) )
1824  {
1825  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
1826  }
1827  if ( properties.contains( QStringLiteral( "svgOutlineColor" ) ) )
1828  {
1829  //pre 2.5 projects used "svgOutlineColor"
1830  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgOutlineColor" )] ) );
1831  }
1832  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
1833  {
1834  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
1835  }
1836  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
1837  {
1838  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
1839  }
1840  if ( properties.contains( QStringLiteral( "svgOutlineWidth" ) ) )
1841  {
1842  //pre 2.5 projects used "svgOutlineWidth"
1843  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "svgOutlineWidth" )].toDouble() );
1844  }
1845  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
1846  {
1847  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
1848  }
1849  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
1850  {
1851  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
1852  }
1853 
1854  //units
1855  if ( properties.contains( QStringLiteral( "pattern_width_unit" ) ) )
1856  {
1857  symbolLayer->setPatternWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "pattern_width_unit" )] ) );
1858  }
1859  if ( properties.contains( QStringLiteral( "pattern_width_map_unit_scale" ) ) )
1860  {
1861  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "pattern_width_map_unit_scale" )] ) );
1862  }
1863  if ( properties.contains( QStringLiteral( "svg_outline_width_unit" ) ) )
1864  {
1865  symbolLayer->setSvgStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "svg_outline_width_unit" )] ) );
1866  }
1867  if ( properties.contains( QStringLiteral( "svg_outline_width_map_unit_scale" ) ) )
1868  {
1869  symbolLayer->setSvgStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "svg_outline_width_map_unit_scale" )] ) );
1870  }
1871  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
1872  {
1873  symbolLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
1874  }
1875  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1876  {
1877  symbolLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1878  }
1879 
1880  symbolLayer->restoreOldDataDefinedProperties( properties );
1881 
1882  return symbolLayer.release();
1883 }
1884 
1886 {
1887  QgsStringMap::iterator it = properties.find( QStringLiteral( "svgFile" ) );
1888  if ( it != properties.end() )
1889  {
1890  if ( saving )
1891  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1892  else
1893  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1894  }
1895 }
1896 
1898 {
1899  return QStringLiteral( "SVGFill" );
1900 }
1901 
1902 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush, const QString &svgFilePath, double patternWidth, QgsUnitTypes::RenderUnit patternWidthUnit,
1903  const QColor &svgFillColor, const QColor &svgStrokeColor, double svgStrokeWidth,
1906 {
1907  if ( mSvgViewBox.isNull() )
1908  {
1909  return;
1910  }
1911 
1912  double size = context.renderContext().convertToPainterUnits( patternWidth, patternWidthUnit, patternWidthMapUnitScale );
1913 
1914  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1915  {
1916  brush.setTextureImage( QImage() );
1917  }
1918  else
1919  {
1920  bool fitsInCache = true;
1921  double strokeWidth = context.renderContext().convertToPainterUnits( svgStrokeWidth, svgStrokeWidthUnit, svgStrokeWidthMapUnitScale );
1922  QImage patternImage = QgsApplication::svgCache()->svgAsImage( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1923  context.renderContext().scaleFactor(), fitsInCache );
1924  if ( !fitsInCache )
1925  {
1926  QPicture patternPict = QgsApplication::svgCache()->svgAsPicture( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1927  context.renderContext().scaleFactor() );
1928  double hwRatio = 1.0;
1929  if ( patternPict.width() > 0 )
1930  {
1931  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1932  }
1933  patternImage = QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1934  patternImage.fill( 0 ); // transparent background
1935 
1936  QPainter p( &patternImage );
1937  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1938  }
1939 
1940  QTransform brushTransform;
1941  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
1942  {
1943  QImage transparentImage = patternImage.copy();
1944  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
1945  brush.setTextureImage( transparentImage );
1946  }
1947  else
1948  {
1949  brush.setTextureImage( patternImage );
1950  }
1951  brush.setTransform( brushTransform );
1952  }
1953 }
1954 
1956 {
1957 
1958  applyPattern( mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit, mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
1959 
1960  if ( mStroke )
1961  {
1962  mStroke->startRender( context.renderContext(), context.fields() );
1963  }
1964 }
1965 
1967 {
1968  if ( mStroke )
1969  {
1970  mStroke->stopRender( context.renderContext() );
1971  }
1972 }
1973 
1975 {
1976  QgsStringMap map;
1977  if ( !mSvgFilePath.isEmpty() )
1978  {
1979  map.insert( QStringLiteral( "svgFile" ), mSvgFilePath );
1980  }
1981  else
1982  {
1983  map.insert( QStringLiteral( "data" ), QString( mSvgData.toHex() ) );
1984  }
1985 
1986  map.insert( QStringLiteral( "width" ), QString::number( mPatternWidth ) );
1987  map.insert( QStringLiteral( "angle" ), QString::number( mAngle ) );
1988 
1989  //svg parameters
1990  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
1991  map.insert( QStringLiteral( "outline_color" ), QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
1992  map.insert( QStringLiteral( "outline_width" ), QString::number( mSvgStrokeWidth ) );
1993 
1994  //units
1995  map.insert( QStringLiteral( "pattern_width_unit" ), QgsUnitTypes::encodeUnit( mPatternWidthUnit ) );
1996  map.insert( QStringLiteral( "pattern_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
1997  map.insert( QStringLiteral( "svg_outline_width_unit" ), QgsUnitTypes::encodeUnit( mSvgStrokeWidthUnit ) );
1998  map.insert( QStringLiteral( "svg_outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSvgStrokeWidthMapUnitScale ) );
1999  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2000  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2001  return map;
2002 }
2003 
2005 {
2006  std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2007  if ( !mSvgFilePath.isEmpty() )
2008  {
2009  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth, mAngle );
2010  clonedLayer->setSvgFillColor( mColor );
2011  clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2012  clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2013  }
2014  else
2015  {
2016  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth, mAngle );
2017  }
2018 
2019  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2020  clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2021  clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2022  clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2023  clonedLayer->setStrokeWidthUnit( mStrokeWidthUnit );
2024  clonedLayer->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2025 
2026  if ( mStroke )
2027  {
2028  clonedLayer->setSubSymbol( mStroke->clone() );
2029  }
2030  copyDataDefinedProperties( clonedLayer.get() );
2031  copyPaintEffect( clonedLayer.get() );
2032  return clonedLayer.release();
2033 }
2034 
2035 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2036 {
2037  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2038  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2039  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2040  element.appendChild( symbolizerElem );
2041 
2042  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2043 
2044  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2045  symbolizerElem.appendChild( fillElem );
2046 
2047  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2048  fillElem.appendChild( graphicFillElem );
2049 
2050  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2051  graphicFillElem.appendChild( graphicElem );
2052 
2053  if ( !mSvgFilePath.isEmpty() )
2054  {
2055  // encode a parametric SVG reference
2056  double patternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2057  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mSvgStrokeWidth, mSvgStrokeWidthUnit, props );
2058  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mSvgFilePath, mColor, patternWidth, mSvgStrokeColor, strokeWidth );
2059  }
2060  else
2061  {
2062  // TODO: create svg from data
2063  // <se:InlineContent>
2064  symbolizerElem.appendChild( doc.createComment( QStringLiteral( "SVG from data not implemented yet" ) ) );
2065  }
2066 
2067  // <Rotation>
2068  QString angleFunc;
2069  bool ok;
2070  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2071  if ( !ok )
2072  {
2073  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2074  }
2075  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2076  {
2077  angleFunc = QString::number( angle + mAngle );
2078  }
2079  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2080 
2081  if ( mStroke )
2082  {
2083  // the stroke sub symbol should be stored within the Stroke element,
2084  // but it will be stored in a separated LineSymbolizer because it could
2085  // have more than one layer
2086  mStroke->toSld( doc, element, props );
2087  }
2088 }
2089 
2091 {
2092  QString path, mimeType;
2093  QColor fillColor, strokeColor;
2094  Qt::PenStyle penStyle;
2095  double size, strokeWidth;
2096 
2097  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2098  if ( fillElem.isNull() )
2099  return nullptr;
2100 
2101  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2102  if ( graphicFillElem.isNull() )
2103  return nullptr;
2104 
2105  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2106  if ( graphicElem.isNull() )
2107  return nullptr;
2108 
2109  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2110  return nullptr;
2111 
2112  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2113  return nullptr;
2114 
2115  QgsSymbolLayerUtils::lineFromSld( graphicElem, penStyle, strokeColor, strokeWidth );
2116 
2117  QString uom = element.attribute( QStringLiteral( "uom" ) );
2118  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2119  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
2120 
2121  double angle = 0.0;
2122  QString angleFunc;
2123  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2124  {
2125  bool ok;
2126  double d = angleFunc.toDouble( &ok );
2127  if ( ok )
2128  angle = d;
2129  }
2130 
2131  std::unique_ptr< QgsSVGFillSymbolLayer > sl = qgis::make_unique< QgsSVGFillSymbolLayer >( path, size, angle );
2132  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2133  sl->setSvgFillColor( fillColor );
2134  sl->setSvgStrokeColor( strokeColor );
2135  sl->setSvgStrokeWidth( strokeWidth );
2136 
2137  // try to get the stroke
2138  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2139  if ( !strokeElem.isNull() )
2140  {
2142  if ( l )
2143  {
2144  QgsSymbolLayerList layers;
2145  layers.append( l );
2146  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2147  }
2148  }
2149 
2150  return sl.release();
2151 }
2152 
2154 {
2158  {
2159  return; //no data defined settings
2160  }
2161 
2163  {
2164  context.setOriginalValueVariable( mAngle );
2166  }
2167 
2168  double width = mPatternWidth;
2170  {
2171  context.setOriginalValueVariable( mPatternWidth );
2173  }
2174  QString svgFile = mSvgFilePath;
2176  {
2177  context.setOriginalValueVariable( mSvgFilePath );
2179  context.renderContext().pathResolver() );
2180  }
2181  QColor svgFillColor = mColor;
2183  {
2186  }
2187  QColor svgStrokeColor = mSvgStrokeColor;
2189  {
2190  context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2192  }
2193  double strokeWidth = mSvgStrokeWidth;
2195  {
2196  context.setOriginalValueVariable( mSvgStrokeWidth );
2198  }
2199  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgStrokeColor, strokeWidth,
2200  mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2201 
2202 }
2203 
2204 void QgsSVGFillSymbolLayer::storeViewBox()
2205 {
2206  if ( !mSvgData.isEmpty() )
2207  {
2208  QSvgRenderer r( mSvgData );
2209  if ( r.isValid() )
2210  {
2211  mSvgViewBox = r.viewBoxF();
2212  return;
2213  }
2214  }
2215 
2216  mSvgViewBox = QRectF();
2217 }
2218 
2219 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2220 {
2221  if ( mSvgFilePath.isEmpty() )
2222  {
2223  return;
2224  }
2225 
2226  bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2227  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2228  QColor defaultFillColor, defaultStrokeColor;
2229  double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2230  QgsApplication::svgCache()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2231  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2232  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2233  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2234  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2235 
2236  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2237  double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2238 
2239  if ( hasDefaultFillColor )
2240  {
2241  mColor = defaultFillColor;
2242  mColor.setAlphaF( newFillOpacity );
2243  }
2244  if ( hasDefaultFillOpacity )
2245  {
2246  mColor.setAlphaF( defaultFillOpacity );
2247  }
2248  if ( hasDefaultStrokeColor )
2249  {
2250  mSvgStrokeColor = defaultStrokeColor;
2251  mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2252  }
2253  if ( hasDefaultStrokeOpacity )
2254  {
2255  mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2256  }
2257  if ( hasDefaultStrokeWidth )
2258  {
2259  mSvgStrokeWidth = defaultStrokeWidth;
2260  }
2261 }
2262 
2263 
2266 {
2267  setSubSymbol( new QgsLineSymbol() );
2268  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2269 }
2270 
2272 {
2273  mFillLineSymbol->setWidth( w );
2274  mLineWidth = w;
2275 }
2276 
2278 {
2279  mFillLineSymbol->setColor( c );
2280  mColor = c;
2281 }
2282 
2284 {
2285  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2286 }
2287 
2289 {
2290  delete mFillLineSymbol;
2291 }
2292 
2294 {
2295  if ( !symbol )
2296  {
2297  return false;
2298  }
2299 
2300  if ( symbol->type() == QgsSymbol::Line )
2301  {
2302  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
2303  if ( lineSymbol )
2304  {
2305  delete mFillLineSymbol;
2306  mFillLineSymbol = lineSymbol;
2307 
2308  return true;
2309  }
2310  }
2311  delete symbol;
2312  return false;
2313 }
2314 
2316 {
2317  return mFillLineSymbol;
2318 }
2319 
2321 {
2322  QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
2323  if ( mFillLineSymbol )
2324  attr.unite( mFillLineSymbol->usedAttributes( context ) );
2325  return attr;
2326 }
2327 
2329 {
2330  return 0;
2331 }
2332 
2334 {
2336  mDistanceUnit = unit;
2337  mLineWidthUnit = unit;
2338  mOffsetUnit = unit;
2339 }
2340 
2342 {
2344  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2345  {
2347  }
2348  return unit;
2349 }
2350 
2352 {
2354  mDistanceMapUnitScale = scale;
2355  mLineWidthMapUnitScale = scale;
2356  mOffsetMapUnitScale = scale;
2357 }
2358 
2360 {
2361  if ( QgsImageFillSymbolLayer::mapUnitScale() == mDistanceMapUnitScale &&
2362  mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2363  mLineWidthMapUnitScale == mOffsetMapUnitScale )
2364  {
2365  return mDistanceMapUnitScale;
2366  }
2367  return QgsMapUnitScale();
2368 }
2369 
2371 {
2372  std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2373 
2374  //default values
2375  double lineAngle = 45;
2376  double distance = 5;
2377  double lineWidth = 0.5;
2378  QColor color( Qt::black );
2379  double offset = 0.0;
2380 
2381  if ( properties.contains( QStringLiteral( "lineangle" ) ) )
2382  {
2383  //pre 2.5 projects used "lineangle"
2384  lineAngle = properties[QStringLiteral( "lineangle" )].toDouble();
2385  }
2386  else if ( properties.contains( QStringLiteral( "angle" ) ) )
2387  {
2388  lineAngle = properties[QStringLiteral( "angle" )].toDouble();
2389  }
2390  patternLayer->setLineAngle( lineAngle );
2391 
2392  if ( properties.contains( QStringLiteral( "distance" ) ) )
2393  {
2394  distance = properties[QStringLiteral( "distance" )].toDouble();
2395  }
2396  patternLayer->setDistance( distance );
2397 
2398  if ( properties.contains( QStringLiteral( "linewidth" ) ) )
2399  {
2400  //pre 2.5 projects used "linewidth"
2401  lineWidth = properties[QStringLiteral( "linewidth" )].toDouble();
2402  }
2403  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
2404  {
2405  lineWidth = properties[QStringLiteral( "outline_width" )].toDouble();
2406  }
2407  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
2408  {
2409  lineWidth = properties[QStringLiteral( "line_width" )].toDouble();
2410  }
2411  patternLayer->setLineWidth( lineWidth );
2412 
2413  if ( properties.contains( QStringLiteral( "color" ) ) )
2414  {
2415  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] );
2416  }
2417  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
2418  {
2419  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] );
2420  }
2421  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
2422  {
2423  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] );
2424  }
2425  patternLayer->setColor( color );
2426 
2427  if ( properties.contains( QStringLiteral( "offset" ) ) )
2428  {
2429  offset = properties[QStringLiteral( "offset" )].toDouble();
2430  }
2431  patternLayer->setOffset( offset );
2432 
2433 
2434  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
2435  {
2436  patternLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )] ) );
2437  }
2438  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
2439  {
2440  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )] ) );
2441  }
2442  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
2443  {
2444  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
2445  }
2446  else if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2447  {
2448  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2449  }
2450  if ( properties.contains( QStringLiteral( "line_width_map_unit_scale" ) ) )
2451  {
2452  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "line_width_map_unit_scale" )] ) );
2453  }
2454  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2455  {
2456  patternLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
2457  }
2458  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2459  {
2460  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
2461  }
2462  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2463  {
2464  patternLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2465  }
2466  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2467  {
2468  patternLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2469  }
2470 
2471  patternLayer->restoreOldDataDefinedProperties( properties );
2472 
2473  return patternLayer.release();
2474 }
2475 
2477 {
2478  return QStringLiteral( "LinePatternFill" );
2479 }
2480 
2481 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double lineAngle, double distance )
2482 {
2483  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2484 
2485  if ( !mFillLineSymbol )
2486  {
2487  return;
2488  }
2489  // We have to make a copy because marker intervals will have to be adjusted
2490  std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2491  if ( !fillLineSymbol )
2492  {
2493  return;
2494  }
2495 
2496  const QgsRenderContext &ctx = context.renderContext();
2497  //double strokePixelWidth = lineWidth * QgsSymbolLayerUtils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2498  double outputPixelDist = ctx.convertToPainterUnits( distance, mDistanceUnit, mDistanceMapUnitScale );
2499  double outputPixelOffset = ctx.convertToPainterUnits( mOffset, mOffsetUnit, mOffsetMapUnitScale );
2500 
2501  // NOTE: this may need to be modified if we ever change from a forced rasterized/brush approach,
2502  // because potentially we may want to allow vector based line pattern fills where the first line
2503  // is offset by a large distance
2504 
2505  // fix truncated pattern with larger offsets
2506  outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2507  if ( outputPixelOffset > outputPixelDist / 2.0 )
2508  outputPixelOffset -= outputPixelDist;
2509 
2510  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2511  // For marker lines we have to get markers interval.
2512  double outputPixelBleed = 0;
2513  double outputPixelInterval = 0; // maximum interval
2514  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2515  {
2516  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2517  double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() );
2518  outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2519 
2520  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2521  if ( markerLineLayer )
2522  {
2523  double outputPixelLayerInterval = ctx.convertToPainterUnits( markerLineLayer->interval(), markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2524 
2525  // There may be multiple marker lines with different intervals.
2526  // In theory we should find the least common multiple, but that could be too
2527  // big (multiplication of intervals in the worst case).
2528  // Because patterns without small common interval would look strange, we
2529  // believe that the longest interval should usually be sufficient.
2530  outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2531  }
2532  }
2533 
2534  if ( outputPixelInterval > 0 )
2535  {
2536  // We have to adjust marker intervals to integer pixel size to get
2537  // repeatable pattern.
2538  double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2539  outputPixelInterval = std::round( outputPixelInterval );
2540 
2541  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2542  {
2543  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2544 
2545  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2546  if ( markerLineLayer )
2547  {
2548  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2549  }
2550  }
2551  }
2552 
2553  //create image
2554  int height, width;
2555  lineAngle = std::fmod( lineAngle, 360 );
2556  if ( lineAngle < 0 )
2557  lineAngle += 360;
2558  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2559  {
2560  height = outputPixelDist;
2561  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2562  }
2563  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2564  {
2565  width = outputPixelDist;
2566  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2567  }
2568  else
2569  {
2570  height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2571  width = outputPixelDist / std::sin( lineAngle * M_PI / 180 );
2572 
2573  // recalculate real angle and distance after rounding to pixels
2574  lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2575  if ( lineAngle < 0 )
2576  {
2577  lineAngle += 360.;
2578  }
2579 
2580  height = std::abs( height );
2581  width = std::abs( width );
2582 
2583  outputPixelDist = std::abs( height * std::cos( lineAngle * M_PI / 180 ) );
2584 
2585  // Round offset to correspond to one pixel height, otherwise lines may
2586  // be shifted on tile border if offset falls close to pixel center
2587  int offsetHeight = static_cast< int >( std::round( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) );
2588  outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 );
2589  }
2590 
2591  //depending on the angle, we might need to render into a larger image and use a subset of it
2592  double dx = 0;
2593  double dy = 0;
2594 
2595  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2596  // thus we add integer multiplications of width and height covering the bleed
2597  int bufferMulti = static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2598 
2599  // Always buffer at least once so that center of line marker in upper right corner
2600  // does not fall outside due to representation error
2601  bufferMulti = std::max( bufferMulti, 1 );
2602 
2603  int xBuffer = width * bufferMulti;
2604  int yBuffer = height * bufferMulti;
2605  int innerWidth = width;
2606  int innerHeight = height;
2607  width += 2 * xBuffer;
2608  height += 2 * yBuffer;
2609 
2610  //protect from zero width/height image and symbol layer from eating too much memory
2611  if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2612  {
2613  return;
2614  }
2615 
2616  QImage patternImage( width, height, QImage::Format_ARGB32 );
2617  patternImage.fill( 0 );
2618 
2619  QPointF p1, p2, p3, p4, p5, p6;
2620  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2621  {
2622  p1 = QPointF( 0, yBuffer );
2623  p2 = QPointF( width, yBuffer );
2624  p3 = QPointF( 0, yBuffer + innerHeight );
2625  p4 = QPointF( width, yBuffer + innerHeight );
2626  }
2627  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2628  {
2629  p1 = QPointF( xBuffer, height );
2630  p2 = QPointF( xBuffer, 0 );
2631  p3 = QPointF( xBuffer + innerWidth, height );
2632  p4 = QPointF( xBuffer + innerWidth, 0 );
2633  }
2634  else if ( lineAngle > 0 && lineAngle < 90 )
2635  {
2636  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2637  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2638  p1 = QPointF( 0, height );
2639  p2 = QPointF( width, 0 );
2640  p3 = QPointF( -dx, height - dy );
2641  p4 = QPointF( width - dx, -dy );
2642  p5 = QPointF( dx, height + dy );
2643  p6 = QPointF( width + dx, dy );
2644  }
2645  else if ( lineAngle > 180 && lineAngle < 270 )
2646  {
2647  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2648  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2649  p1 = QPointF( width, 0 );
2650  p2 = QPointF( 0, height );
2651  p3 = QPointF( width - dx, -dy );
2652  p4 = QPointF( -dx, height - dy );
2653  p5 = QPointF( width + dx, dy );
2654  p6 = QPointF( dx, height + dy );
2655  }
2656  else if ( lineAngle > 90 && lineAngle < 180 )
2657  {
2658  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2659  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2660  p1 = QPointF( 0, 0 );
2661  p2 = QPointF( width, height );
2662  p5 = QPointF( dx, -dy );
2663  p6 = QPointF( width + dx, height - dy );
2664  p3 = QPointF( -dx, dy );
2665  p4 = QPointF( width - dx, height + dy );
2666  }
2667  else if ( lineAngle > 270 && lineAngle < 360 )
2668  {
2669  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2670  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2671  p1 = QPointF( width, height );
2672  p2 = QPointF( 0, 0 );
2673  p5 = QPointF( width + dx, height - dy );
2674  p6 = QPointF( dx, -dy );
2675  p3 = QPointF( width - dx, height + dy );
2676  p4 = QPointF( -dx, dy );
2677  }
2678 
2679  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2680  {
2681  QPointF tempPt;
2682  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2683  p3 = QPointF( tempPt.x(), tempPt.y() );
2684  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2685  p4 = QPointF( tempPt.x(), tempPt.y() );
2686  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2687  p5 = QPointF( tempPt.x(), tempPt.y() );
2688  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2689  p6 = QPointF( tempPt.x(), tempPt.y() );
2690 
2691  //update p1, p2 last
2692  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2693  p1 = QPointF( tempPt.x(), tempPt.y() );
2694  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2695  p2 = QPointF( tempPt.x(), tempPt.y() );
2696  }
2697 
2698  QPainter p( &patternImage );
2699 
2700 #if 0
2701  // DEBUG: Draw rectangle
2702  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2703  QPen pen( QColor( Qt::black ) );
2704  pen.setWidthF( 0.1 );
2705  pen.setCapStyle( Qt::FlatCap );
2706  p.setPen( pen );
2707 
2708  // To see this rectangle, comment buffer cut below.
2709  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2710  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2711  p.drawPolygon( polygon );
2712 
2713  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 );
2714  p.drawPolygon( polygon );
2715 #endif
2716 
2717  // Use antialiasing because without antialiasing lines are rendered to the
2718  // right and below the mathematically defined points (not symmetrical)
2719  // and such tiles become useless for are filling
2720  p.setRenderHint( QPainter::Antialiasing, true );
2721 
2722  // line rendering needs context for drawing on patternImage
2723  QgsRenderContext lineRenderContext;
2724  lineRenderContext.setPainter( &p );
2725  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
2727  lineRenderContext.setMapToPixel( mtp );
2728  lineRenderContext.setForceVectorOutput( false );
2729  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2730 
2731  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2732 
2733  QVector<QPolygonF> polygons;
2734  polygons.append( QPolygonF() << p1 << p2 );
2735  polygons.append( QPolygonF() << p3 << p4 );
2736  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2737  {
2738  polygons.append( QPolygonF() << p5 << p6 );
2739  }
2740 
2741  for ( const QPolygonF &polygon : qgis::as_const( polygons ) )
2742  {
2743  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2744  }
2745 
2746  fillLineSymbol->stopRender( lineRenderContext );
2747  p.end();
2748 
2749  // Cut off the buffer
2750  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2751 
2752  //set image to mBrush
2753  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2754  {
2755  QImage transparentImage = patternImage.copy();
2756  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2757  brush.setTextureImage( transparentImage );
2758  }
2759  else
2760  {
2761  brush.setTextureImage( patternImage );
2762  }
2763 
2764  QTransform brushTransform;
2765  brush.setTransform( brushTransform );
2766 }
2767 
2769 {
2770  applyPattern( context, mBrush, mLineAngle, mDistance );
2771 
2772  if ( mFillLineSymbol )
2773  {
2774  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2775  }
2776 }
2777 
2779 {
2780  if ( mFillLineSymbol )
2781  {
2782  mFillLineSymbol->stopRender( context.renderContext() );
2783  }
2784 }
2785 
2787 {
2788  QgsStringMap map;
2789  map.insert( QStringLiteral( "angle" ), QString::number( mLineAngle ) );
2790  map.insert( QStringLiteral( "distance" ), QString::number( mDistance ) );
2791  map.insert( QStringLiteral( "line_width" ), QString::number( mLineWidth ) );
2792  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2793  map.insert( QStringLiteral( "offset" ), QString::number( mOffset ) );
2794  map.insert( QStringLiteral( "distance_unit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
2795  map.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineWidthUnit ) );
2796  map.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
2797  map.insert( QStringLiteral( "distance_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2798  map.insert( QStringLiteral( "line_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2799  map.insert( QStringLiteral( "offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2800  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2801  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2802  return map;
2803 }
2804 
2806 {
2808  if ( mFillLineSymbol )
2809  {
2810  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2811  }
2812  copyPaintEffect( clonedLayer );
2813  copyDataDefinedProperties( clonedLayer );
2814  return clonedLayer;
2815 }
2816 
2817 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2818 {
2819  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2820  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2821  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2822  element.appendChild( symbolizerElem );
2823 
2824  // <Geometry>
2825  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2826 
2827  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2828  symbolizerElem.appendChild( fillElem );
2829 
2830  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2831  fillElem.appendChild( graphicFillElem );
2832 
2833  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2834  graphicFillElem.appendChild( graphicElem );
2835 
2836  //line properties must be inside the graphic definition
2837  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2838  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2839  lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2840  double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
2841  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "horline" ), QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2842 
2843  // <Rotation>
2844  QString angleFunc;
2845  bool ok;
2846  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2847  if ( !ok )
2848  {
2849  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mLineAngle );
2850  }
2851  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2852  {
2853  angleFunc = QString::number( angle + mLineAngle );
2854  }
2855  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2856 
2857  // <se:Displacement>
2858  QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2859  lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
2860  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
2861 }
2862 
2863 QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFactor ) const
2864 {
2865  QString featureStyle;
2866  featureStyle.append( "Brush(" );
2867  featureStyle.append( QStringLiteral( "fc:%1" ).arg( mColor.name() ) );
2868  featureStyle.append( QStringLiteral( ",bc:%1" ).arg( QStringLiteral( "#00000000" ) ) ); //transparent background
2869  featureStyle.append( ",id:\"ogr-brush-2\"" );
2870  featureStyle.append( QStringLiteral( ",a:%1" ).arg( mLineAngle ) );
2871  featureStyle.append( QStringLiteral( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2872  featureStyle.append( ",dx:0mm" );
2873  featureStyle.append( QStringLiteral( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2874  featureStyle.append( ')' );
2875  return featureStyle;
2876 }
2877 
2879 {
2881  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2882  {
2883  return; //no data defined settings
2884  }
2885 
2886  double lineAngle = mLineAngle;
2888  {
2889  context.setOriginalValueVariable( mLineAngle );
2891  }
2892  double distance = mDistance;
2894  {
2895  context.setOriginalValueVariable( mDistance );
2897  }
2898  applyPattern( context, mBrush, lineAngle, distance );
2899 }
2900 
2902 {
2903  QString name;
2904  QColor fillColor, lineColor;
2905  double size, lineWidth;
2906  Qt::PenStyle lineStyle;
2907 
2908  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2909  if ( fillElem.isNull() )
2910  return nullptr;
2911 
2912  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2913  if ( graphicFillElem.isNull() )
2914  return nullptr;
2915 
2916  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2917  if ( graphicElem.isNull() )
2918  return nullptr;
2919 
2920  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2921  return nullptr;
2922 
2923  if ( name != QLatin1String( "horline" ) )
2924  return nullptr;
2925 
2926  double angle = 0.0;
2927  QString angleFunc;
2928  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2929  {
2930  bool ok;
2931  double d = angleFunc.toDouble( &ok );
2932  if ( ok )
2933  angle = d;
2934  }
2935 
2936  double offset = 0.0;
2937  QPointF vectOffset;
2938  if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) )
2939  {
2940  offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
2941  }
2942 
2943  QString uom = element.attribute( QStringLiteral( "uom" ) );
2944  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2945  lineWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, lineWidth );
2946 
2947  std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2948  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2949  sl->setColor( lineColor );
2950  sl->setLineWidth( lineWidth );
2951  sl->setLineAngle( angle );
2952  sl->setOffset( offset );
2953  sl->setDistance( size );
2954 
2955  // try to get the stroke
2956  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2957  if ( !strokeElem.isNull() )
2958  {
2960  if ( l )
2961  {
2962  QgsSymbolLayerList layers;
2963  layers.append( l );
2964  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2965  }
2966  }
2967 
2968  return sl.release();
2969 }
2970 
2971 
2973 
2976 {
2977  mDistanceX = 15;
2978  mDistanceY = 15;
2979  mDisplacementX = 0;
2980  mDisplacementY = 0;
2981  setSubSymbol( new QgsMarkerSymbol() );
2982  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2983 }
2984 
2986 {
2987  delete mMarkerSymbol;
2988 }
2989 
2991 {
2993  mDistanceXUnit = unit;
2994  mDistanceYUnit = unit;
2995  mDisplacementXUnit = unit;
2996  mDisplacementYUnit = unit;
2997  if ( mMarkerSymbol )
2998  {
2999  mMarkerSymbol->setOutputUnit( unit );
3000  }
3001 
3002 }
3003 
3005 {
3007  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3008  {
3010  }
3011  return unit;
3012 }
3013 
3015 {
3017  mDistanceXMapUnitScale = scale;
3018  mDistanceYMapUnitScale = scale;
3021 }
3022 
3024 {
3029  {
3030  return mDistanceXMapUnitScale;
3031  }
3032  return QgsMapUnitScale();
3033 }
3034 
3036 {
3037  std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = qgis::make_unique< QgsPointPatternFillSymbolLayer >();
3038  if ( properties.contains( QStringLiteral( "distance_x" ) ) )
3039  {
3040  layer->setDistanceX( properties[QStringLiteral( "distance_x" )].toDouble() );
3041  }
3042  if ( properties.contains( QStringLiteral( "distance_y" ) ) )
3043  {
3044  layer->setDistanceY( properties[QStringLiteral( "distance_y" )].toDouble() );
3045  }
3046  if ( properties.contains( QStringLiteral( "displacement_x" ) ) )
3047  {
3048  layer->setDisplacementX( properties[QStringLiteral( "displacement_x" )].toDouble() );
3049  }
3050  if ( properties.contains( QStringLiteral( "displacement_y" ) ) )
3051  {
3052  layer->setDisplacementY( properties[QStringLiteral( "displacement_y" )].toDouble() );
3053  }
3054 
3055  if ( properties.contains( QStringLiteral( "distance_x_unit" ) ) )
3056  {
3057  layer->setDistanceXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_x_unit" )] ) );
3058  }
3059  if ( properties.contains( QStringLiteral( "distance_x_map_unit_scale" ) ) )
3060  {
3061  layer->setDistanceXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_x_map_unit_scale" )] ) );
3062  }
3063  if ( properties.contains( QStringLiteral( "distance_y_unit" ) ) )
3064  {
3065  layer->setDistanceYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_y_unit" )] ) );
3066  }
3067  if ( properties.contains( QStringLiteral( "distance_y_map_unit_scale" ) ) )
3068  {
3069  layer->setDistanceYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_y_map_unit_scale" )] ) );
3070  }
3071  if ( properties.contains( QStringLiteral( "displacement_x_unit" ) ) )
3072  {
3073  layer->setDisplacementXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_x_unit" )] ) );
3074  }
3075  if ( properties.contains( QStringLiteral( "displacement_x_map_unit_scale" ) ) )
3076  {
3077  layer->setDisplacementXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_x_map_unit_scale" )] ) );
3078  }
3079  if ( properties.contains( QStringLiteral( "displacement_y_unit" ) ) )
3080  {
3081  layer->setDisplacementYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_y_unit" )] ) );
3082  }
3083  if ( properties.contains( QStringLiteral( "displacement_y_map_unit_scale" ) ) )
3084  {
3085  layer->setDisplacementYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_y_map_unit_scale" )] ) );
3086  }
3087  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
3088  {
3089  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
3090  }
3091  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3092  {
3093  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3094  }
3095 
3096  layer->restoreOldDataDefinedProperties( properties );
3097 
3098  return layer.release();
3099 }
3100 
3102 {
3103  return QStringLiteral( "PointPatternFill" );
3104 }
3105 
3106 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double distanceX, double distanceY,
3107  double displacementX, double displacementY )
3108 {
3109  //render 3 rows and columns in one go to easily incorporate displacement
3110  const QgsRenderContext &ctx = context.renderContext();
3113 
3114  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3115  {
3116  QImage img;
3117  brush.setTextureImage( img );
3118  return;
3119  }
3120 
3121  QImage patternImage( width, height, QImage::Format_ARGB32 );
3122  patternImage.fill( 0 );
3123 
3124  if ( mMarkerSymbol )
3125  {
3126  QPainter p( &patternImage );
3127 
3128  //marker rendering needs context for drawing on patternImage
3129  QgsRenderContext pointRenderContext;
3130  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3131  pointRenderContext.setPainter( &p );
3132  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
3134  {
3135  pointRenderContext.setFlag( QgsRenderContext::Antialiasing, true );
3136  p.setRenderHint( QPainter::Antialiasing, true );
3137  }
3139  pointRenderContext.setMapToPixel( mtp );
3140  pointRenderContext.setForceVectorOutput( false );
3141  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3142 
3143  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3144 
3145  //render corner points
3146  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3147  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3148  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3149  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3150 
3151  //render displaced points
3153  double displacementPixelY = ctx.convertToPainterUnits( displacementY, mDisplacementYUnit, mDisplacementYMapUnitScale );
3154  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3155  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3156  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3157  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3158  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3159 
3160  mMarkerSymbol->stopRender( pointRenderContext );
3161  }
3162 
3163  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
3164  {
3165  QImage transparentImage = patternImage.copy();
3166  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
3167  brush.setTextureImage( transparentImage );
3168  }
3169  else
3170  {
3171  brush.setTextureImage( patternImage );
3172  }
3173  QTransform brushTransform;
3174  brush.setTransform( brushTransform );
3175 }
3176 
3178 {
3179  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3180 
3181  if ( mStroke )
3182  {
3183  mStroke->startRender( context.renderContext(), context.fields() );
3184  }
3185 }
3186 
3188 {
3189  if ( mStroke )
3190  {
3191  mStroke->stopRender( context.renderContext() );
3192  }
3193 }
3194 
3196 {
3197  QgsStringMap map;
3198  map.insert( QStringLiteral( "distance_x" ), QString::number( mDistanceX ) );
3199  map.insert( QStringLiteral( "distance_y" ), QString::number( mDistanceY ) );
3200  map.insert( QStringLiteral( "displacement_x" ), QString::number( mDisplacementX ) );
3201  map.insert( QStringLiteral( "displacement_y" ), QString::number( mDisplacementY ) );
3202  map.insert( QStringLiteral( "distance_x_unit" ), QgsUnitTypes::encodeUnit( mDistanceXUnit ) );
3203  map.insert( QStringLiteral( "distance_y_unit" ), QgsUnitTypes::encodeUnit( mDistanceYUnit ) );
3204  map.insert( QStringLiteral( "displacement_x_unit" ), QgsUnitTypes::encodeUnit( mDisplacementXUnit ) );
3205  map.insert( QStringLiteral( "displacement_y_unit" ), QgsUnitTypes::encodeUnit( mDisplacementYUnit ) );
3206  map.insert( QStringLiteral( "distance_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3207  map.insert( QStringLiteral( "distance_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3208  map.insert( QStringLiteral( "displacement_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3209  map.insert( QStringLiteral( "displacement_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3210  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
3211  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
3212  return map;
3213 }
3214 
3216 {
3218  if ( mMarkerSymbol )
3219  {
3220  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3221  }
3222  copyDataDefinedProperties( clonedLayer );
3223  copyPaintEffect( clonedLayer );
3224  return clonedLayer;
3225 }
3226 
3227 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3228 {
3229  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3230  {
3231  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
3232  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
3233  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
3234  element.appendChild( symbolizerElem );
3235 
3236  // <Geometry>
3237  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
3238 
3239  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
3240  symbolizerElem.appendChild( fillElem );
3241 
3242  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
3243  fillElem.appendChild( graphicFillElem );
3244 
3245  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3248  QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
3249  QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "distance" ), dist );
3250  symbolizerElem.appendChild( distanceElem );
3251 
3252  QgsSymbolLayer *layer = mMarkerSymbol->symbolLayer( i );
3253  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
3254  if ( !markerLayer )
3255  {
3256  QString errorMsg = QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3257  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3258  }
3259  else
3260  {
3261  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3262  }
3263  }
3264 }
3265 
3267 {
3268  Q_UNUSED( element );
3269  return nullptr;
3270 }
3271 
3273 {
3274  if ( !symbol )
3275  {
3276  return false;
3277  }
3278 
3279  if ( symbol->type() == QgsSymbol::Marker )
3280  {
3281  QgsMarkerSymbol *markerSymbol = static_cast<QgsMarkerSymbol *>( symbol );
3282  delete mMarkerSymbol;
3283  mMarkerSymbol = markerSymbol;
3284  }
3285  return true;
3286 }
3287 
3289 {
3293  {
3294  return;
3295  }
3296 
3297  double distanceX = mDistanceX;
3299  {
3302  }
3303  double distanceY = mDistanceY;
3305  {
3308  }
3309  double displacementX = mDisplacementX;
3311  {
3314  }
3315  double displacementY = mDisplacementY;
3317  {
3320  }
3321  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3322 }
3323 
3325 {
3326  return 0;
3327 }
3328 
3330 {
3331  QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes( context );
3332 
3333  if ( mMarkerSymbol )
3334  attributes.unite( mMarkerSymbol->usedAttributes( context ) );
3335 
3336  return attributes;
3337 }
3338 
3340 {
3341  mColor = c;
3342  if ( mMarkerSymbol )
3343  mMarkerSymbol->setColor( c );
3344 }
3345 
3347 {
3348  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3349 }
3350 
3352 
3353 
3355 {
3356  setSubSymbol( new QgsMarkerSymbol() );
3357 }
3358 
3360 {
3361  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3362 
3363  if ( properties.contains( QStringLiteral( "point_on_surface" ) ) )
3364  sl->setPointOnSurface( properties[QStringLiteral( "point_on_surface" )].toInt() != 0 );
3365  if ( properties.contains( QStringLiteral( "point_on_all_parts" ) ) )
3366  sl->setPointOnAllParts( properties[QStringLiteral( "point_on_all_parts" )].toInt() != 0 );
3367 
3368  sl->restoreOldDataDefinedProperties( properties );
3369 
3370  return sl.release();
3371 }
3372 
3374 {
3375  return QStringLiteral( "CentroidFill" );
3376 }
3377 
3379 {
3380  mMarker->setColor( color );
3381  mColor = color;
3382 }
3383 
3385 {
3386  return mMarker ? mMarker->color() : mColor;
3387 }
3388 
3390 {
3391  mMarker->setOpacity( context.opacity() );
3392  mMarker->startRender( context.renderContext(), context.fields() );
3393 
3394  mCurrentFeatureId = -1;
3395  mBiggestPartIndex = 0;
3396 }
3397 
3399 {
3400  mMarker->stopRender( context.renderContext() );
3401 }
3402 
3403 void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3404 {
3405  Q_UNUSED( rings );
3406 
3407  if ( !mPointOnAllParts )
3408  {
3409  const QgsFeature *feature = context.feature();
3410  if ( feature )
3411  {
3412  if ( feature->id() != mCurrentFeatureId )
3413  {
3414  mCurrentFeatureId = feature->id();
3415  mBiggestPartIndex = 1;
3416 
3417  if ( context.geometryPartCount() > 1 )
3418  {
3419  const QgsGeometry geom = feature->geometry();
3420  const QgsGeometryCollection *geomCollection = static_cast<const QgsGeometryCollection *>( geom.constGet() );
3421 
3422  double area = 0;
3423  double areaBiggest = 0;
3424  for ( int i = 0; i < context.geometryPartCount(); ++i )
3425  {
3426  area = geomCollection->geometryN( i )->area();
3427  if ( area > areaBiggest )
3428  {
3429  areaBiggest = area;
3430  mBiggestPartIndex = i + 1;
3431  }
3432  }
3433  }
3434  }
3435  }
3436  }
3437 
3438  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3439  {
3440  QPointF centroid = mPointOnSurface ? QgsSymbolLayerUtils::polygonPointOnSurface( points ) : QgsSymbolLayerUtils::polygonCentroid( points );
3441  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3442  }
3443 }
3444 
3446 {
3447  QgsStringMap map;
3448  map[QStringLiteral( "point_on_surface" )] = QString::number( mPointOnSurface );
3449  map[QStringLiteral( "point_on_all_parts" )] = QString::number( mPointOnAllParts );
3450  return map;
3451 }
3452 
3454 {
3455  std::unique_ptr< QgsCentroidFillSymbolLayer > x = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3456  x->mAngle = mAngle;
3457  x->mColor = mColor;
3458  x->setSubSymbol( mMarker->clone() );
3459  x->setPointOnSurface( mPointOnSurface );
3460  x->setPointOnAllParts( mPointOnAllParts );
3461  copyDataDefinedProperties( x.get() );
3462  copyPaintEffect( x.get() );
3463  return x.release();
3464 }
3465 
3466 void QgsCentroidFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3467 {
3468  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3469  // used with PointSymbolizer, then the semantic is to use the centroid
3470  // of the geometry, or any similar representative point.
3471  mMarker->toSld( doc, element, props );
3472 }
3473 
3475 {
3477  if ( !l )
3478  return nullptr;
3479 
3480  QgsSymbolLayerList layers;
3481  layers.append( l );
3482  std::unique_ptr< QgsMarkerSymbol > marker( new QgsMarkerSymbol( layers ) );
3483 
3484  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3485  sl->setSubSymbol( marker.release() );
3486  return sl.release();
3487 }
3488 
3489 
3491 {
3492  return mMarker.get();
3493 }
3494 
3496 {
3497  if ( !symbol || symbol->type() != QgsSymbol::Marker )
3498  {
3499  delete symbol;
3500  return false;
3501  }
3502 
3503  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
3504  mColor = mMarker->color();
3505  return true;
3506 }
3507 
3509 {
3510  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
3511 
3512  if ( mMarker )
3513  attributes.unite( mMarker->usedAttributes( context ) );
3514 
3515  return attributes;
3516 }
3517 
3519 {
3520  if ( mMarker )
3521  {
3522  mMarker->setOutputUnit( unit );
3523  }
3524 }
3525 
3527 {
3528  if ( mMarker )
3529  {
3530  return mMarker->outputUnit();
3531  }
3532  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
3533 }
3534 
3536 {
3537  if ( mMarker )
3538  {
3539  mMarker->setMapUnitScale( scale );
3540  }
3541 }
3542 
3544 {
3545  if ( mMarker )
3546  {
3547  return mMarker->mapUnitScale();
3548  }
3549  return QgsMapUnitScale();
3550 }
3551 
3552 
3553 
3554 
3557  , mImageFilePath( imageFilePath )
3558 {
3559  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3560 }
3561 
3563 {
3565  double alpha = 1.0;
3566  QPointF offset;
3567  double angle = 0.0;
3568  double width = 0.0;
3569 
3570  QString imagePath;
3571  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3572  {
3573  imagePath = properties[QStringLiteral( "imageFile" )];
3574  }
3575  if ( properties.contains( QStringLiteral( "coordinate_mode" ) ) )
3576  {
3577  mode = static_cast< FillCoordinateMode >( properties[QStringLiteral( "coordinate_mode" )].toInt() );
3578  }
3579  if ( properties.contains( QStringLiteral( "alpha" ) ) )
3580  {
3581  alpha = properties[QStringLiteral( "alpha" )].toDouble();
3582  }
3583  if ( properties.contains( QStringLiteral( "offset" ) ) )
3584  {
3585  offset = QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] );
3586  }
3587  if ( properties.contains( QStringLiteral( "angle" ) ) )
3588  {
3589  angle = properties[QStringLiteral( "angle" )].toDouble();
3590  }
3591  if ( properties.contains( QStringLiteral( "width" ) ) )
3592  {
3593  width = properties[QStringLiteral( "width" )].toDouble();
3594  }
3595  std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = qgis::make_unique< QgsRasterFillSymbolLayer >( imagePath );
3596  symbolLayer->setCoordinateMode( mode );
3597  symbolLayer->setOpacity( alpha );
3598  symbolLayer->setOffset( offset );
3599  symbolLayer->setAngle( angle );
3600  symbolLayer->setWidth( width );
3601  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3602  {
3603  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
3604  }
3605  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3606  {
3607  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
3608  }
3609  if ( properties.contains( QStringLiteral( "width_unit" ) ) )
3610  {
3611  symbolLayer->setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "width_unit" )] ) );
3612  }
3613  if ( properties.contains( QStringLiteral( "width_map_unit_scale" ) ) )
3614  {
3615  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "width_map_unit_scale" )] ) );
3616  }
3617 
3618  symbolLayer->restoreOldDataDefinedProperties( properties );
3619 
3620  return symbolLayer.release();
3621 }
3622 
3624 {
3625  QgsStringMap::iterator it = properties.find( QStringLiteral( "imageFile" ) );
3626  if ( it != properties.end() )
3627  {
3628  if ( saving )
3629  it.value() = pathResolver.writePath( it.value() );
3630  else
3631  it.value() = pathResolver.readPath( it.value() );
3632  }
3633 }
3634 
3636 {
3637  Q_UNUSED( symbol );
3638  return true;
3639 }
3640 
3642 {
3643  return QStringLiteral( "RasterFill" );
3644 }
3645 
3646 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3647 {
3648  QPainter *p = context.renderContext().painter();
3649  if ( !p )
3650  {
3651  return;
3652  }
3653 
3654  QPointF offset;
3655  if ( !mOffset.isNull() )
3656  {
3657  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
3658  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
3659  p->translate( offset );
3660  }
3661  if ( mCoordinateMode == Feature )
3662  {
3663  QRectF boundingRect = points.boundingRect();
3664  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3665  boundingRect.top() - mBrush.transform().dy() ) );
3666  }
3667 
3668  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3669  if ( !mOffset.isNull() )
3670  {
3671  p->translate( -offset );
3672  }
3673 }
3674 
3676 {
3677  applyPattern( mBrush, mImageFilePath, mWidth, mOpacity, context );
3678 }
3679 
3681 {
3682  Q_UNUSED( context );
3683 }
3684 
3686 {
3687  QgsStringMap map;
3688  map[QStringLiteral( "imageFile" )] = mImageFilePath;
3689  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
3690  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3691  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3692  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3693  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3694  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3695  map[QStringLiteral( "width" )] = QString::number( mWidth );
3696  map[QStringLiteral( "width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
3697  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
3698  return map;
3699 }
3700 
3702 {
3703  std::unique_ptr< QgsRasterFillSymbolLayer > sl = qgis::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
3704  sl->setCoordinateMode( mCoordinateMode );
3705  sl->setOpacity( mOpacity );
3706  sl->setOffset( mOffset );
3707  sl->setOffsetUnit( mOffsetUnit );
3708  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
3709  sl->setAngle( mAngle );
3710  sl->setWidth( mWidth );
3711  sl->setWidthUnit( mWidthUnit );
3712  sl->setWidthMapUnitScale( mWidthMapUnitScale );
3713  copyDataDefinedProperties( sl.get() );
3714  copyPaintEffect( sl.get() );
3715  return sl.release();
3716 }
3717 
3719 {
3720  return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
3721 }
3722 
3723 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
3724 {
3725  mImageFilePath = imagePath;
3726 }
3727 
3729 {
3730  mCoordinateMode = mode;
3731 }
3732 
3734 {
3735  mOpacity = opacity;
3736 }
3737 
3739 {
3740  if ( !dataDefinedProperties().hasActiveProperties() )
3741  return; // shortcut
3742 
3743  bool hasWidthExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
3744  bool hasFileExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFile );
3745  bool hasOpacityExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity );
3746  bool hasAngleExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
3747 
3748  if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
3749  {
3750  return; //no data defined settings
3751  }
3752 
3753  bool ok;
3754  if ( hasAngleExpression )
3755  {
3756  context.setOriginalValueVariable( mAngle );
3758  if ( ok )
3759  mNextAngle = nextAngle;
3760  }
3761 
3762  if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
3763  {
3764  return; //nothing further to do
3765  }
3766 
3767  double width = mWidth;
3768  if ( hasWidthExpression )
3769  {
3770  context.setOriginalValueVariable( mWidth );
3772  }
3773  double opacity = mOpacity;
3774  if ( hasOpacityExpression )
3775  {
3776  context.setOriginalValueVariable( mOpacity );
3778  }
3779  QString file = mImageFilePath;
3780  if ( hasFileExpression )
3781  {
3782  context.setOriginalValueVariable( mImageFilePath );
3784  }
3785  applyPattern( mBrush, file, width, opacity, context );
3786 }
3787 
3788 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolRenderContext &context )
3789 {
3790  QImage image( imageFilePath );
3791  if ( image.isNull() )
3792  {
3793  return;
3794  }
3795  if ( !image.hasAlphaChannel() )
3796  {
3797  image = image.convertToFormat( QImage::Format_ARGB32 );
3798  }
3799 
3800  double pixelWidth;
3801  if ( width > 0 )
3802  {
3803  pixelWidth = context.renderContext().convertToPainterUnits( width, mWidthUnit, mWidthMapUnitScale );
3804  }
3805  else
3806  {
3807  pixelWidth = image.width();
3808  }
3809 
3810  //reduce alpha of image
3811  if ( alpha < 1.0 )
3812  {
3813  QPainter p;
3814  p.begin( &image );
3815  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3816  QColor alphaColor( 0, 0, 0 );
3817  alphaColor.setAlphaF( alpha );
3818  p.fillRect( image.rect(), alphaColor );
3819  p.end();
3820  }
3821 
3822  //resize image if required
3823  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3824  {
3825  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3826  }
3827 
3828  brush.setTextureImage( image );
3829 }
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.
#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:64
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...
Gradient reference point 1 is centroid.
QgsSimpleFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
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
Returns the path to the SVG file used to render the fill.
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)
QgsSVGFillSymbolLayer(const QString &svgFilePath, double width=20, double rotation=0.0)
Constructor for QgsSVGFillSymbolLayer, using the SVG picture at the specified absolute file path...
#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:653
QgsMapUnitScale mStrokeWidthMapUnitScale
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsRasterFillSymbolLayer from a properties map.
QgsRasterFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void stopRender(QgsSymbolRenderContext &context) override
void setSvgFillColor(const QColor &c)
Sets the fill color used for rendering the SVG content.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
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.
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.
Qt::PenJoinStyle penJoinStyle() const
GradientSpread gradientSpread() const
Gradient spread mode. Controls how the gradient behaves outside of the predefined stops...
A symbol fill consisting of repeated parallel lines.
QgsUnitTypes::RenderUnit mOffsetUnit
double angle() const
QgsMapUnitScale mapUnitScale() const override
Base class for polygon renderers generating texture images.
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used to draw the shapeburst fill.
void startRender(QgsSymbolRenderContext &context) override
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:278
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 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.
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
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
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())
Creates a new QgsLinePatternFillSymbolLayer from a properties map.
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)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
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())
Creates a new QgsSVGFillSymbolLayer from a properties map.
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.
Flags flags() const
Returns combination of flags used for rendering.
static QgsSymbolLayer * createFromSld(QDomElement &element)
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:150
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
#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
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:55
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.
GradientColorType mGradientColorType
const QgsMapUnitScale & svgStrokeWidthMapUnitScale() const
Returns the map unit scale for the pattern&#39;s stroke.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
QColor color() const override
The fill color.
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:570
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSVGFillSymbolLayer from a SLD element.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
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:404
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Gradient reference point 2 y.
double lineAngle() const
Returns the angle for the parallel lines used to fill the symbol.
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 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:259
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:1522
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:1097
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
double offset() const
Returns the offset distance for lines within the fill, which is the distance to offset the parallel l...
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.
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.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
#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.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
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.
Geometry collection.
void setWidth(double width)
Definition: qgssymbol.cpp:1601
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
static QgsSymbolLayer * createFromSld(QDomElement &element)
double width() const
Definition: qgssymbol.cpp:1627
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
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 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:51
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:459
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)
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
Returns the stroke color used for rendering the SVG content.
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:572
void stopRender(QgsSymbolRenderContext &context) override
QColor color() const override
The fill color.
Tiling is based on feature bounding box.
QgsSymbolLayer * symbolLayer(int layer)
Returns a specific symbol layer contained in the symbol.
Definition: qgssymbol.cpp:340
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
static Qt::PenStyle decodePenStyle(const QString &str)
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
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:611
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
double lineWidth() const
Returns the width of the line subsymbol used to render the parallel lines in the fill.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsShapeburstFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Gradient reference point 1 y.
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 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:671
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())
Constructor for QgsRasterFillSymbolLayer, using a raster fill from the specified imageFilePath.
void setLineWidth(double w)
Sets the width of the line subsymbol used to render the parallel lines in the fill.
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.
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 filling symbols with a repeated SVG file.
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:654
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).
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:668
QColor svgFillColor() const
Returns the fill color used for rendering the SVG content.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
~QgsShapeburstFillSymbolLayer() override
Struct for storing maximum and minimum scales for measurements in map units.
static QString encodeBrushStyle(Qt::BrushStyle style)
const QgsMapUnitScale & patternWidthMapUnitScale() const
Returns the map unit scale for the pattern&#39;s width.
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)
Sets the path to the SVG file to render in the fill.
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
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:628
#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.
double patternWidth() const
Returns the width of the rendered SVG content within the fill (i.e.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsLinePatternFillSymbolLayer from a SLD element.
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 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())
QgsGeometry geometry
Definition: qgsfeature.h:67
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
Returns the stroke width used for rendering the SVG content.
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
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.
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 addStopsToGradient(QGradient *gradient, double opacity=1)
Copy color ramp stops to a QGradient.
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.
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:426
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.
FillCoordinateMode
Fill coordinate modes, dictates fill tiling behavior.
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)
QgsMapUnitScale mOffsetMapUnitScale
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
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:659
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1578
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:110
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
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1798
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)
Sets the color for the symbol.
Definition: qgssymbol.cpp:450
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Vertical distance between points.