QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 "qgsimagecache.h"
25 #include "qgsrendercontext.h"
26 #include "qgsproject.h"
27 #include "qgssvgcache.h"
28 #include "qgslogger.h"
29 #include "qgscolorramp.h"
30 #include "qgsunittypes.h"
31 #include "qgsmessagelog.h"
32 #include "qgsapplication.h"
33 #include "qgsimageoperation.h"
34 #include "qgspolygon.h"
35 #include "qgslinestring.h"
37 
38 #include <QPainter>
39 #include <QFile>
40 #include <QSvgRenderer>
41 #include <QDomDocument>
42 #include <QDomElement>
43 #include <random>
44 
45 #ifndef QT_NO_PRINTER
46 #include <QPrinter>
47 #endif
48 
49 QgsSimpleFillSymbolLayer::QgsSimpleFillSymbolLayer( const QColor &color, Qt::BrushStyle style, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth,
50  Qt::PenJoinStyle penJoinStyle )
51  : mBrushStyle( style )
52  , mStrokeColor( strokeColor )
53  , mStrokeStyle( strokeStyle )
54  , mStrokeWidth( strokeWidth )
55  , mPenJoinStyle( penJoinStyle )
56 {
57  mColor = color;
58 }
59 
61 {
62  mStrokeWidthUnit = unit;
63  mOffsetUnit = unit;
64 }
65 
67 {
69  if ( mOffsetUnit != unit )
70  {
72  }
73  return unit;
74 }
75 
77 {
79  mOffsetMapUnitScale = scale;
80 }
81 
83 {
85  {
87  }
88  return QgsMapUnitScale();
89 }
90 
91 void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
92 {
93  if ( !dataDefinedProperties().hasActiveProperties() )
94  return; // shortcut
95 
96  bool ok;
97 
99  {
102  }
104  {
107  if ( exprVal.isValid() )
108  brush.setStyle( QgsSymbolLayerUtils::decodeBrushStyle( exprVal.toString() ) );
109  }
111  {
114  }
116  {
119  double width = exprVal.toDouble( &ok );
120  if ( ok )
121  {
123  pen.setWidthF( width );
124  selPen.setWidthF( width );
125  }
126  }
128  {
131  if ( ok )
132  {
133  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
134  selPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
135  }
136  }
138  {
141  if ( ok )
142  {
143  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
144  selPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
145  }
146  }
147 }
148 
149 
151 {
153  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
157  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
158  QPointF offset;
159 
160  if ( props.contains( QStringLiteral( "color" ) ) )
161  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
162  if ( props.contains( QStringLiteral( "style" ) ) )
163  style = QgsSymbolLayerUtils::decodeBrushStyle( props[QStringLiteral( "style" )] );
164  if ( props.contains( QStringLiteral( "color_border" ) ) )
165  {
166  //pre 2.5 projects used "color_border"
167  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
168  }
169  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
170  {
171  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
172  }
173  else if ( props.contains( QStringLiteral( "line_color" ) ) )
174  {
175  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
176  }
177 
178  if ( props.contains( QStringLiteral( "style_border" ) ) )
179  {
180  //pre 2.5 projects used "style_border"
181  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "style_border" )] );
182  }
183  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
184  {
185  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] );
186  }
187  else if ( props.contains( QStringLiteral( "line_style" ) ) )
188  {
189  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] );
190  }
191  if ( props.contains( QStringLiteral( "width_border" ) ) )
192  {
193  //pre 2.5 projects used "width_border"
194  strokeWidth = props[QStringLiteral( "width_border" )].toDouble();
195  }
196  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
197  {
198  strokeWidth = props[QStringLiteral( "outline_width" )].toDouble();
199  }
200  else if ( props.contains( QStringLiteral( "line_width" ) ) )
201  {
202  strokeWidth = props[QStringLiteral( "line_width" )].toDouble();
203  }
204  if ( props.contains( QStringLiteral( "offset" ) ) )
205  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
206  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
207  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
208 
209  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( color, style, strokeColor, strokeStyle, strokeWidth, penJoinStyle );
210  sl->setOffset( offset );
211  if ( props.contains( QStringLiteral( "border_width_unit" ) ) )
212  {
213  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "border_width_unit" )] ) );
214  }
215  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
216  {
217  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
218  }
219  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
220  {
221  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
222  }
223  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
224  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
225 
226  if ( props.contains( QStringLiteral( "border_width_map_unit_scale" ) ) )
227  sl->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "border_width_map_unit_scale" )] ) );
228  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
229  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
230 
231  sl->restoreOldDataDefinedProperties( props );
232 
233  return sl.release();
234 }
235 
236 
238 {
239  return QStringLiteral( "SimpleFill" );
240 }
241 
243 {
244  QColor fillColor = mColor;
245  fillColor.setAlphaF( context.opacity() * mColor.alphaF() );
246  mBrush = QBrush( fillColor, mBrushStyle );
247 
248  QColor selColor = context.renderContext().selectionColor();
249  QColor selPenColor = selColor == mColor ? selColor : mStrokeColor;
250  if ( ! SELECTION_IS_OPAQUE )
251  selColor.setAlphaF( context.opacity() );
252  mSelBrush = QBrush( selColor );
253  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
254  // this would mean symbols with "no fill" look the same whether or not they are selected
255  if ( SELECT_FILL_STYLE )
256  mSelBrush.setStyle( mBrushStyle );
257 
258  QColor strokeColor = mStrokeColor;
259  strokeColor.setAlphaF( context.opacity() * mStrokeColor.alphaF() );
260  mPen = QPen( strokeColor );
261  mSelPen = QPen( selPenColor );
262  mPen.setStyle( mStrokeStyle );
264  mPen.setJoinStyle( mPenJoinStyle );
265 }
266 
268 {
269  Q_UNUSED( context )
270 }
271 
272 void QgsSimpleFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
273 {
274  QPainter *p = context.renderContext().painter();
275  if ( !p )
276  {
277  return;
278  }
279 
280  applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );
281 
282  QPointF offset = mOffset;
283 
285  {
287  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
288  bool ok = false;
289  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
290  if ( ok )
291  offset = res;
292  }
293 
294  if ( !offset.isNull() )
295  {
298  p->translate( offset );
299  }
300 
301 #ifndef QT_NO_PRINTER
302  if ( mBrush.style() == Qt::SolidPattern || mBrush.style() == Qt::NoBrush || !dynamic_cast<QPrinter *>( p->device() ) )
303 #endif
304  {
305  p->setPen( context.selected() ? mSelPen : mPen );
306  p->setBrush( context.selected() ? mSelBrush : mBrush );
307  _renderPolygon( p, points, rings, context );
308  }
309 #ifndef QT_NO_PRINTER
310  else
311  {
312  // workaround upstream issue https://github.com/qgis/QGIS/issues/36580
313  // when a non-solid brush is set with opacity, the opacity incorrectly applies to the pen
314  // when exporting to PDF/print devices
315  p->setBrush( context.selected() ? mSelBrush : mBrush );
316  p->setPen( Qt::NoPen );
317  _renderPolygon( p, points, rings, context );
318 
319  p->setPen( context.selected() ? mSelPen : mPen );
320  p->setBrush( Qt::NoBrush );
321  _renderPolygon( p, points, rings, context );
322  }
323 #endif
324 
325  if ( !offset.isNull() )
326  {
327  p->translate( -offset );
328  }
329 }
330 
332 {
333  QgsStringMap map;
334  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
335  map[QStringLiteral( "style" )] = QgsSymbolLayerUtils::encodeBrushStyle( mBrushStyle );
336  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
337  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
338  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
339  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
340  map[QStringLiteral( "border_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
341  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
342  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
343  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
344  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
345  return map;
346 }
347 
349 {
350  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( mColor, mBrushStyle, mStrokeColor, mStrokeStyle, mStrokeWidth, mPenJoinStyle );
351  sl->setOffset( mOffset );
352  sl->setOffsetUnit( mOffsetUnit );
353  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
354  sl->setStrokeWidthUnit( mStrokeWidthUnit );
355  sl->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
356  copyDataDefinedProperties( sl.get() );
357  copyPaintEffect( sl.get() );
358  return sl.release();
359 }
360 
361 void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
362 {
363  if ( mBrushStyle == Qt::NoBrush && mStrokeStyle == Qt::NoPen )
364  return;
365 
366  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
367  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
368  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
369  element.appendChild( symbolizerElem );
370 
371  // <Geometry>
372  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
373 
374  if ( mBrushStyle != Qt::NoBrush )
375  {
376  // <Fill>
377  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
378  symbolizerElem.appendChild( fillElem );
380  }
381 
382  if ( mStrokeStyle != Qt::NoPen )
383  {
384  // <Stroke>
385  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
386  symbolizerElem.appendChild( strokeElem );
389  }
390 
391  // <se:Displacement>
394 }
395 
396 QString QgsSimpleFillSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
397 {
398  //brush
399  QString symbolStyle;
400  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStyleBrush( mColor ) );
401  symbolStyle.append( ';' );
402  //pen
403  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStylePen( mStrokeWidth, mmScaleFactor, mapUnitScaleFactor, mStrokeColor, mPenJoinStyle ) );
404  return symbolStyle;
405 }
406 
408 {
409  QColor color, strokeColor;
410  Qt::BrushStyle fillStyle;
411  Qt::PenStyle strokeStyle;
412  double strokeWidth;
413 
414  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
415  QgsSymbolLayerUtils::fillFromSld( fillElem, fillStyle, color );
416 
417  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
419 
420  QPointF offset;
422 
423  QString uom = element.attribute( QStringLiteral( "uom" ), QString() );
427 
428  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( color, fillStyle, strokeColor, strokeStyle, strokeWidth );
429  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
430  sl->setOffset( offset );
431  return sl.release();
432 }
433 
435 {
436  double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale );
437  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
438  return penBleed + offsetBleed;
439 }
440 
442 {
443  double width = mStrokeWidth;
445  {
448  }
450 }
451 
453 {
454  QColor c = mStrokeColor;
456  {
459  }
460  return c;
461 }
462 
464 {
465  double angle = mAngle;
467  {
468  context.setOriginalValueVariable( mAngle );
470  }
471  return angle;
472 }
473 
475 {
476  return mStrokeStyle;
477 }
478 
480 {
481  QColor c = mColor;
483  {
485  }
486  return c;
487 }
488 
490 {
491  return mBrushStyle;
492 }
493 
494 //QgsGradientFillSymbolLayer
495 
496 QgsGradientFillSymbolLayer::QgsGradientFillSymbolLayer( const QColor &color, const QColor &color2,
497  GradientColorType colorType, GradientType gradientType,
498  GradientCoordinateMode coordinateMode, GradientSpread spread )
499  : mGradientColorType( colorType )
500  , mGradientType( gradientType )
501  , mCoordinateMode( coordinateMode )
502  , mGradientSpread( spread )
503  , mReferencePoint1( QPointF( 0.5, 0 ) )
504  , mReferencePoint2( QPointF( 0.5, 1 ) )
505 {
506  mColor = color;
507  mColor2 = color2;
508 }
509 
511 {
512  delete mGradientRamp;
513 }
514 
516 {
517  //default to a two-color, linear gradient with feature mode and pad spreading
522  //default to gradient from the default fill color to white
523  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
524  QPointF referencePoint1 = QPointF( 0.5, 0 );
525  bool refPoint1IsCentroid = false;
526  QPointF referencePoint2 = QPointF( 0.5, 1 );
527  bool refPoint2IsCentroid = false;
528  double angle = 0;
529  QPointF offset;
530 
531  //update gradient properties from props
532  if ( props.contains( QStringLiteral( "type" ) ) )
533  type = static_cast< GradientType >( props[QStringLiteral( "type" )].toInt() );
534  if ( props.contains( QStringLiteral( "coordinate_mode" ) ) )
535  coordinateMode = static_cast< GradientCoordinateMode >( props[QStringLiteral( "coordinate_mode" )].toInt() );
536  if ( props.contains( QStringLiteral( "spread" ) ) )
537  gradientSpread = static_cast< GradientSpread >( props[QStringLiteral( "spread" )].toInt() );
538  if ( props.contains( QStringLiteral( "color_type" ) ) )
539  colorType = static_cast< GradientColorType >( props[QStringLiteral( "color_type" )].toInt() );
540  if ( props.contains( QStringLiteral( "gradient_color" ) ) )
541  {
542  //pre 2.5 projects used "gradient_color"
543  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color" )] );
544  }
545  else if ( props.contains( QStringLiteral( "color" ) ) )
546  {
547  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
548  }
549  if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
550  {
551  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
552  }
553 
554  if ( props.contains( QStringLiteral( "reference_point1" ) ) )
555  referencePoint1 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point1" )] );
556  if ( props.contains( QStringLiteral( "reference_point1_iscentroid" ) ) )
557  refPoint1IsCentroid = props[QStringLiteral( "reference_point1_iscentroid" )].toInt();
558  if ( props.contains( QStringLiteral( "reference_point2" ) ) )
559  referencePoint2 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point2" )] );
560  if ( props.contains( QStringLiteral( "reference_point2_iscentroid" ) ) )
561  refPoint2IsCentroid = props[QStringLiteral( "reference_point2_iscentroid" )].toInt();
562  if ( props.contains( QStringLiteral( "angle" ) ) )
563  angle = props[QStringLiteral( "angle" )].toDouble();
564 
565  if ( props.contains( QStringLiteral( "offset" ) ) )
566  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
567 
568  //attempt to create color ramp from props
569  QgsColorRamp *gradientRamp = nullptr;
570  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QgsCptCityColorRamp::typeString() )
571  {
572  gradientRamp = QgsCptCityColorRamp::create( props );
573  }
574  else
575  {
576  gradientRamp = QgsGradientColorRamp::create( props );
577  }
578 
579  //create a new gradient fill layer with desired properties
580  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( color, color2, colorType, type, coordinateMode, gradientSpread );
581  sl->setOffset( offset );
582  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
583  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
584  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
585  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
586  sl->setReferencePoint1( referencePoint1 );
587  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
588  sl->setReferencePoint2( referencePoint2 );
589  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
590  sl->setAngle( angle );
591  if ( gradientRamp )
592  sl->setColorRamp( gradientRamp );
593 
594  sl->restoreOldDataDefinedProperties( props );
595 
596  return sl.release();
597 }
598 
600 {
601  delete mGradientRamp;
602  mGradientRamp = ramp;
603 }
604 
606 {
607  return QStringLiteral( "GradientFill" );
608 }
609 
610 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, const QPolygonF &points )
611 {
612  if ( !dataDefinedProperties().hasActiveProperties() && !mReferencePoint1IsCentroid && !mReferencePoint2IsCentroid )
613  {
614  //shortcut
617  return;
618  }
619 
620  bool ok;
621 
622  //first gradient color
623  QColor color = mColor;
625  {
628  }
629 
630  //second gradient color
631  QColor color2 = mColor2;
633  {
636  }
637 
638  //gradient rotation angle
639  double angle = mAngle;
641  {
642  context.setOriginalValueVariable( mAngle );
644  }
645 
646  //gradient type
649  {
651  if ( ok )
652  {
653  if ( currentType == QObject::tr( "linear" ) )
654  {
656  }
657  else if ( currentType == QObject::tr( "radial" ) )
658  {
660  }
661  else if ( currentType == QObject::tr( "conical" ) )
662  {
664  }
665  }
666  }
667 
668  //coordinate mode
671  {
672  QString currentCoordMode = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCoordinateMode, context.renderContext().expressionContext(), QString(), &ok );
673  if ( ok )
674  {
675  if ( currentCoordMode == QObject::tr( "feature" ) )
676  {
678  }
679  else if ( currentCoordMode == QObject::tr( "viewport" ) )
680  {
682  }
683  }
684  }
685 
686  //gradient spread
689  {
691  if ( ok )
692  {
693  if ( currentSpread == QObject::tr( "pad" ) )
694  {
696  }
697  else if ( currentSpread == QObject::tr( "repeat" ) )
698  {
700  }
701  else if ( currentSpread == QObject::tr( "reflect" ) )
702  {
704  }
705  }
706  }
707 
708  //reference point 1 x & y
709  double refPoint1X = mReferencePoint1.x();
711  {
712  context.setOriginalValueVariable( refPoint1X );
714  }
715  double refPoint1Y = mReferencePoint1.y();
717  {
718  context.setOriginalValueVariable( refPoint1Y );
720  }
721  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
723  {
724  context.setOriginalValueVariable( refPoint1IsCentroid );
726  }
727 
728  //reference point 2 x & y
729  double refPoint2X = mReferencePoint2.x();
731  {
732  context.setOriginalValueVariable( refPoint2X );
734  }
735  double refPoint2Y = mReferencePoint2.y();
737  {
738  context.setOriginalValueVariable( refPoint2Y );
740  }
741  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
743  {
744  context.setOriginalValueVariable( refPoint2IsCentroid );
746  }
747 
748  if ( refPoint1IsCentroid || refPoint2IsCentroid )
749  {
750  //either the gradient is starting or ending at a centroid, so calculate it
751  QPointF centroid = QgsSymbolLayerUtils::polygonCentroid( points );
752  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
753  QRectF bbox = points.boundingRect();
754  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
755  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
756 
757  if ( refPoint1IsCentroid )
758  {
759  refPoint1X = centroidX;
760  refPoint1Y = centroidY;
761  }
762  if ( refPoint2IsCentroid )
763  {
764  refPoint2X = centroidX;
765  refPoint2Y = centroidY;
766  }
767  }
768 
769  //update gradient with data defined values
771  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
772 }
773 
774 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint, double angle )
775 {
776  //rotate a reference point by a specified angle around the point (0.5, 0.5)
777 
778  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
779  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
780  //rotate this line by the current rotation angle
781  refLine.setAngle( refLine.angle() + angle );
782  //get new end point of line
783  QPointF rotatedReferencePoint = refLine.p2();
784  //make sure coords of new end point is within [0, 1]
785  if ( rotatedReferencePoint.x() > 1 )
786  rotatedReferencePoint.setX( 1 );
787  if ( rotatedReferencePoint.x() < 0 )
788  rotatedReferencePoint.setX( 0 );
789  if ( rotatedReferencePoint.y() > 1 )
790  rotatedReferencePoint.setY( 1 );
791  if ( rotatedReferencePoint.y() < 0 )
792  rotatedReferencePoint.setY( 0 );
793 
794  return rotatedReferencePoint;
795 }
796 
797 void QgsGradientFillSymbolLayer::applyGradient( const QgsSymbolRenderContext &context, QBrush &brush,
798  const QColor &color, const QColor &color2, GradientColorType gradientColorType,
799  QgsColorRamp *gradientRamp, GradientType gradientType,
800  GradientCoordinateMode coordinateMode, GradientSpread gradientSpread,
801  QPointF referencePoint1, QPointF referencePoint2, const double angle )
802 {
803  //update alpha of gradient colors
804  QColor fillColor = color;
805  fillColor.setAlphaF( context.opacity() * fillColor.alphaF() );
806  QColor fillColor2 = color2;
807  fillColor2.setAlphaF( context.opacity() * fillColor2.alphaF() );
808 
809  //rotate reference points
810  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
811  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
812 
813  //create a QGradient with the desired properties
814  QGradient gradient;
815  switch ( gradientType )
816  {
818  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
819  break;
821  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
822  break;
824  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
825  break;
826  }
827  switch ( coordinateMode )
828  {
830  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
831  break;
833  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
834  break;
835  }
836  switch ( gradientSpread )
837  {
839  gradient.setSpread( QGradient::PadSpread );
840  break;
842  gradient.setSpread( QGradient::ReflectSpread );
843  break;
845  gradient.setSpread( QGradient::RepeatSpread );
846  break;
847  }
848 
849  //add stops to gradient
851  ( gradientRamp->type() == QgsGradientColorRamp::typeString() || gradientRamp->type() == QgsCptCityColorRamp::typeString() ) )
852  {
853  //color ramp gradient
854  QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( gradientRamp );
855  gradRamp->addStopsToGradient( &gradient, context.opacity() );
856  }
857  else
858  {
859  //two color gradient
860  gradient.setColorAt( 0.0, fillColor );
861  gradient.setColorAt( 1.0, fillColor2 );
862  }
863 
864  //update QBrush use gradient
865  brush = QBrush( gradient );
866 }
867 
869 {
870  QColor selColor = context.renderContext().selectionColor();
871  if ( ! SELECTION_IS_OPAQUE )
872  selColor.setAlphaF( context.opacity() );
873  mSelBrush = QBrush( selColor );
874 }
875 
877 {
878  Q_UNUSED( context )
879 }
880 
881 void QgsGradientFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
882 {
883  QPainter *p = context.renderContext().painter();
884  if ( !p )
885  {
886  return;
887  }
888 
889  applyDataDefinedSymbology( context, points );
890 
891  p->setBrush( context.selected() ? mSelBrush : mBrush );
892  p->setPen( Qt::NoPen );
893 
894  QPointF offset = mOffset;
896  {
898  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
899  bool ok = false;
900  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
901  if ( ok )
902  offset = res;
903  }
904 
905  if ( !offset.isNull() )
906  {
909  p->translate( offset );
910  }
911 
912  _renderPolygon( p, points, rings, context );
913 
914  if ( !offset.isNull() )
915  {
916  p->translate( -offset );
917  }
918 }
919 
921 {
922  QgsStringMap map;
923  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
924  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
925  map[QStringLiteral( "color_type" )] = QString::number( mGradientColorType );
926  map[QStringLiteral( "type" )] = QString::number( mGradientType );
927  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
928  map[QStringLiteral( "spread" )] = QString::number( mGradientSpread );
929  map[QStringLiteral( "reference_point1" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint1 );
930  map[QStringLiteral( "reference_point1_iscentroid" )] = QString::number( mReferencePoint1IsCentroid );
931  map[QStringLiteral( "reference_point2" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint2 );
932  map[QStringLiteral( "reference_point2_iscentroid" )] = QString::number( mReferencePoint2IsCentroid );
933  map[QStringLiteral( "angle" )] = QString::number( mAngle );
934  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
935  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
936  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
937  if ( mGradientRamp )
938  {
939 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
940  map.unite( mGradientRamp->properties() );
941 #else
942  map.insert( mGradientRamp->properties() );
943 #endif
944  }
945  return map;
946 }
947 
949 {
950  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( mColor, mColor2, mGradientColorType, mGradientType, mCoordinateMode, mGradientSpread );
951  if ( mGradientRamp )
952  sl->setColorRamp( mGradientRamp->clone() );
953  sl->setReferencePoint1( mReferencePoint1 );
954  sl->setReferencePoint1IsCentroid( mReferencePoint1IsCentroid );
955  sl->setReferencePoint2( mReferencePoint2 );
956  sl->setReferencePoint2IsCentroid( mReferencePoint2IsCentroid );
957  sl->setAngle( mAngle );
958  sl->setOffset( mOffset );
959  sl->setOffsetUnit( mOffsetUnit );
960  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
961  copyDataDefinedProperties( sl.get() );
962  copyPaintEffect( sl.get() );
963  return sl.release();
964 }
965 
967 {
968  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
969  return offsetBleed;
970 }
971 
973 {
974  mOffsetUnit = unit;
975 }
976 
978 {
979  return mOffsetUnit;
980 }
981 
983 {
984  mOffsetMapUnitScale = scale;
985 }
986 
988 {
989  return mOffsetMapUnitScale;
990 }
991 
992 //QgsShapeburstFillSymbolLayer
993 
994 QgsShapeburstFillSymbolLayer::QgsShapeburstFillSymbolLayer( const QColor &color, const QColor &color2, ShapeburstColorType colorType,
995  int blurRadius, bool useWholeShape, double maxDistance )
996  : mBlurRadius( blurRadius )
997  , mUseWholeShape( useWholeShape )
998  , mMaxDistance( maxDistance )
999  , mColorType( colorType )
1000  , mColor2( color2 )
1001 {
1002  mColor = color;
1003 }
1004 
1006 
1008 {
1009  //default to a two-color gradient
1011  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
1012  int blurRadius = 0;
1013  bool useWholeShape = true;
1014  double maxDistance = 5;
1015  QPointF offset;
1016 
1017  //update fill properties from props
1018  if ( props.contains( QStringLiteral( "color_type" ) ) )
1019  {
1020  colorType = static_cast< ShapeburstColorType >( props[QStringLiteral( "color_type" )].toInt() );
1021  }
1022  if ( props.contains( QStringLiteral( "shapeburst_color" ) ) )
1023  {
1024  //pre 2.5 projects used "shapeburst_color"
1025  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color" )] );
1026  }
1027  else if ( props.contains( QStringLiteral( "color" ) ) )
1028  {
1029  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
1030  }
1031 
1032  if ( props.contains( QStringLiteral( "shapeburst_color2" ) ) )
1033  {
1034  //pre 2.5 projects used "shapeburst_color2"
1035  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color2" )] );
1036  }
1037  else if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
1038  {
1039  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
1040  }
1041  if ( props.contains( QStringLiteral( "blur_radius" ) ) )
1042  {
1043  blurRadius = props[QStringLiteral( "blur_radius" )].toInt();
1044  }
1045  if ( props.contains( QStringLiteral( "use_whole_shape" ) ) )
1046  {
1047  useWholeShape = props[QStringLiteral( "use_whole_shape" )].toInt();
1048  }
1049  if ( props.contains( QStringLiteral( "max_distance" ) ) )
1050  {
1051  maxDistance = props[QStringLiteral( "max_distance" )].toDouble();
1052  }
1053  if ( props.contains( QStringLiteral( "offset" ) ) )
1054  {
1055  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
1056  }
1057 
1058  //attempt to create color ramp from props
1059  QgsColorRamp *gradientRamp = nullptr;
1060  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QgsCptCityColorRamp::typeString() )
1061  {
1062  gradientRamp = QgsCptCityColorRamp::create( props );
1063  }
1064  else
1065  {
1066  gradientRamp = QgsGradientColorRamp::create( props );
1067  }
1068 
1069  //create a new shapeburst fill layer with desired properties
1070  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1071  sl->setOffset( offset );
1072  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1073  {
1074  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1075  }
1076  if ( props.contains( QStringLiteral( "distance_unit" ) ) )
1077  {
1078  sl->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "distance_unit" )] ) );
1079  }
1080  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1081  {
1082  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1083  }
1084  if ( props.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
1085  {
1086  sl->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "distance_map_unit_scale" )] ) );
1087  }
1088  if ( props.contains( QStringLiteral( "ignore_rings" ) ) )
1089  {
1090  sl->setIgnoreRings( props[QStringLiteral( "ignore_rings" )].toInt() );
1091  }
1092  if ( gradientRamp )
1093  {
1094  sl->setColorRamp( gradientRamp );
1095  }
1096 
1097  sl->restoreOldDataDefinedProperties( props );
1098 
1099  return sl.release();
1100 }
1101 
1103 {
1104  return QStringLiteral( "ShapeburstFill" );
1105 }
1106 
1108 {
1109  if ( mGradientRamp.get() == ramp )
1110  return;
1111 
1112  mGradientRamp.reset( ramp );
1113 }
1114 
1115 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QColor &color, QColor &color2, int &blurRadius, bool &useWholeShape,
1116  double &maxDistance, bool &ignoreRings )
1117 {
1118  //first gradient color
1119  color = mColor;
1121  {
1124  }
1125 
1126  //second gradient color
1127  color2 = mColor2;
1129  {
1132  }
1133 
1134  //blur radius
1135  blurRadius = mBlurRadius;
1137  {
1138  context.setOriginalValueVariable( mBlurRadius );
1140  }
1141 
1142  //use whole shape
1143  useWholeShape = mUseWholeShape;
1145  {
1146  context.setOriginalValueVariable( mUseWholeShape );
1148  }
1149 
1150  //max distance
1151  maxDistance = mMaxDistance;
1153  {
1154  context.setOriginalValueVariable( mMaxDistance );
1156  }
1157 
1158  //ignore rings
1159  ignoreRings = mIgnoreRings;
1161  {
1162  context.setOriginalValueVariable( mIgnoreRings );
1164  }
1165 
1166 }
1167 
1169 {
1170  //TODO - check this
1171  QColor selColor = context.renderContext().selectionColor();
1172  if ( ! SELECTION_IS_OPAQUE )
1173  selColor.setAlphaF( context.opacity() );
1174  mSelBrush = QBrush( selColor );
1175 }
1176 
1178 {
1179  Q_UNUSED( context )
1180 }
1181 
1182 void QgsShapeburstFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
1183 {
1184  QPainter *p = context.renderContext().painter();
1185  if ( !p )
1186  {
1187  return;
1188  }
1189 
1190  if ( context.selected() )
1191  {
1192  //feature is selected, draw using selection style
1193  p->setBrush( mSelBrush );
1194  QPointF offset = mOffset;
1195 
1197  {
1199  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
1200  bool ok = false;
1201  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
1202  if ( ok )
1203  offset = res;
1204  }
1205 
1206  if ( !offset.isNull() )
1207  {
1208  offset.setX( context.renderContext().convertToPainterUnits( offset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1209  offset.setY( context.renderContext().convertToPainterUnits( offset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1210  p->translate( offset );
1211  }
1212  _renderPolygon( p, points, rings, context );
1213  if ( !offset.isNull() )
1214  {
1215  p->translate( -offset );
1216  }
1217  return;
1218  }
1219 
1220  QColor color1, color2;
1221  int blurRadius;
1222  bool useWholeShape;
1223  double maxDistance;
1224  bool ignoreRings;
1225  //calculate data defined symbology
1226  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1227 
1228  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1229  int outputPixelMaxDist = 0;
1230  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1231  {
1232  //convert max distance to pixels
1233  outputPixelMaxDist = static_cast< int >( std::round( context.renderContext().convertToPainterUnits( maxDistance, mDistanceUnit, mDistanceMapUnitScale ) ) );
1234  }
1235 
1236  //if we are using the two color mode, create a gradient ramp
1237  std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1239  {
1240  twoColorGradientRamp = qgis::make_unique< QgsGradientColorRamp >( color1, color2 );
1241  }
1242 
1243  //no stroke for shapeburst fills
1244  p->setPen( QPen( Qt::NoPen ) );
1245 
1246  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1247  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1248  //create a QImage to draw shapeburst in
1249  int pointsWidth = static_cast< int >( std::round( points.boundingRect().width() ) );
1250  int pointsHeight = static_cast< int >( std::round( points.boundingRect().height() ) );
1251  int imWidth = pointsWidth + ( sideBuffer * 2 );
1252  int imHeight = pointsHeight + ( sideBuffer * 2 );
1253  std::unique_ptr< QImage > fillImage = qgis::make_unique< QImage >( imWidth,
1254  imHeight, QImage::Format_ARGB32_Premultiplied );
1255  if ( fillImage->isNull() )
1256  {
1257  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1258  return;
1259  }
1260 
1261  //also create an image to store the alpha channel
1262  std::unique_ptr< QImage > alphaImage = qgis::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1263  if ( alphaImage->isNull() )
1264  {
1265  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1266  return;
1267  }
1268 
1269  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1270  //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
1271  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1272  fillImage->fill( Qt::black );
1273 
1274  //initially fill the alpha channel image with a transparent color
1275  alphaImage->fill( Qt::transparent );
1276 
1277  //now, draw the polygon in the alpha channel image
1278  QPainter imgPainter;
1279  imgPainter.begin( alphaImage.get() );
1280  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1281  imgPainter.setBrush( QBrush( Qt::white ) );
1282  imgPainter.setPen( QPen( Qt::black ) );
1283  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1284  _renderPolygon( &imgPainter, points, rings, context );
1285  imgPainter.end();
1286 
1287  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1288  //(this avoids calling _renderPolygon twice, since that can be slow)
1289  imgPainter.begin( fillImage.get() );
1290  if ( !ignoreRings )
1291  {
1292  imgPainter.drawImage( 0, 0, *alphaImage );
1293  }
1294  else
1295  {
1296  //using ignore rings mode, so the alpha image can't be used
1297  //directly as the alpha channel contains polygon rings and we need
1298  //to draw now without any rings
1299  imgPainter.setBrush( QBrush( Qt::white ) );
1300  imgPainter.setPen( QPen( Qt::black ) );
1301  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1302  _renderPolygon( &imgPainter, points, nullptr, context );
1303  }
1304  imgPainter.end();
1305 
1306  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1307  double *dtArray = distanceTransform( fillImage.get(), context.renderContext() );
1308 
1309  //copy distance transform values back to QImage, shading by appropriate color ramp
1310  dtArrayToQImage( dtArray, fillImage.get(), mColorType == QgsShapeburstFillSymbolLayer::SimpleTwoColor ? twoColorGradientRamp.get() : mGradientRamp.get(),
1311  context.renderContext(), useWholeShape, outputPixelMaxDist );
1312  if ( context.opacity() < 1 )
1313  {
1314  QgsImageOperation::multiplyOpacity( *fillImage, context.opacity() );
1315  }
1316 
1317  //clean up some variables
1318  delete [] dtArray;
1319 
1320  //apply blur if desired
1321  if ( blurRadius > 0 )
1322  {
1323  QgsImageOperation::stackBlur( *fillImage, blurRadius, false );
1324  }
1325 
1326  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1327  imgPainter.begin( fillImage.get() );
1328  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1329  imgPainter.drawImage( 0, 0, *alphaImage );
1330  imgPainter.end();
1331  //we're finished with the alpha channel image now
1332  alphaImage.reset();
1333 
1334  //draw shapeburst image in correct place in the destination painter
1335 
1336  QgsScopedQPainterState painterState( p );
1337  QPointF offset = mOffset;
1339  {
1341  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
1342  bool ok = false;
1343  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
1344  if ( ok )
1345  offset = res;
1346  }
1347  if ( !offset.isNull() )
1348  {
1349  offset.setX( context.renderContext().convertToPainterUnits( offset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1350  offset.setY( context.renderContext().convertToPainterUnits( offset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1351  p->translate( offset );
1352  }
1353 
1354  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1355 
1356  if ( !offset.isNull() )
1357  {
1358  p->translate( -offset );
1359  }
1360 }
1361 
1362 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1363 
1364 /* distance transform of a 1d function using squared distance */
1365 void QgsShapeburstFillSymbolLayer::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1366 {
1367  int k = 0;
1368  v[0] = 0;
1369  z[0] = -INF;
1370  z[1] = + INF;
1371  for ( int q = 1; q <= n - 1; q++ )
1372  {
1373  double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1374  while ( s <= z[k] )
1375  {
1376  k--;
1377  s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1378  }
1379  k++;
1380  v[k] = q;
1381  z[k] = s;
1382  z[k + 1] = + INF;
1383  }
1384 
1385  k = 0;
1386  for ( int q = 0; q <= n - 1; q++ )
1387  {
1388  while ( z[k + 1] < q )
1389  k++;
1390  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1391  }
1392 }
1393 
1394 /* distance transform of 2d function using squared distance */
1395 void QgsShapeburstFillSymbolLayer::distanceTransform2d( double *im, int width, int height, QgsRenderContext &context )
1396 {
1397  int maxDimension = std::max( width, height );
1398  double *f = new double[ maxDimension ];
1399  int *v = new int[ maxDimension ];
1400  double *z = new double[ maxDimension + 1 ];
1401  double *d = new double[ maxDimension ];
1402 
1403  // transform along columns
1404  for ( int x = 0; x < width; x++ )
1405  {
1406  if ( context.renderingStopped() )
1407  break;
1408 
1409  for ( int y = 0; y < height; y++ )
1410  {
1411  f[y] = im[ x + y * width ];
1412  }
1413  distanceTransform1d( f, height, v, z, d );
1414  for ( int y = 0; y < height; y++ )
1415  {
1416  im[ x + y * width ] = d[y];
1417  }
1418  }
1419 
1420  // transform along rows
1421  for ( int y = 0; y < height; y++ )
1422  {
1423  if ( context.renderingStopped() )
1424  break;
1425 
1426  for ( int x = 0; x < width; x++ )
1427  {
1428  f[x] = im[ x + y * width ];
1429  }
1430  distanceTransform1d( f, width, v, z, d );
1431  for ( int x = 0; x < width; x++ )
1432  {
1433  im[ x + y * width ] = d[x];
1434  }
1435  }
1436 
1437  delete [] d;
1438  delete [] f;
1439  delete [] v;
1440  delete [] z;
1441 }
1442 
1443 /* distance transform of a binary QImage */
1444 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im, QgsRenderContext &context )
1445 {
1446  int width = im->width();
1447  int height = im->height();
1448 
1449  double *dtArray = new double[width * height];
1450 
1451  //load qImage to array
1452  QRgb tmpRgb;
1453  int idx = 0;
1454  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1455  {
1456  if ( context.renderingStopped() )
1457  break;
1458 
1459  const QRgb *scanLine = reinterpret_cast< const QRgb * >( im->constScanLine( heightIndex ) );
1460  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1461  {
1462  tmpRgb = scanLine[widthIndex];
1463  if ( qRed( tmpRgb ) == 0 )
1464  {
1465  //black pixel, so zero distance
1466  dtArray[ idx ] = 0;
1467  }
1468  else
1469  {
1470  //white pixel, so initially set distance as infinite
1471  dtArray[ idx ] = INF;
1472  }
1473  idx++;
1474  }
1475  }
1476 
1477  //calculate squared distance transform
1478  distanceTransform2d( dtArray, width, height, context );
1479 
1480  return dtArray;
1481 }
1482 
1483 void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, QgsColorRamp *ramp, QgsRenderContext &context, bool useWholeShape, int maxPixelDistance )
1484 {
1485  int width = im->width();
1486  int height = im->height();
1487 
1488  //find maximum distance value
1489  double maxDistanceValue;
1490 
1491  if ( useWholeShape )
1492  {
1493  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1494  double dtMaxValue = array[0];
1495  for ( int i = 1; i < ( width * height ); ++i )
1496  {
1497  if ( array[i] > dtMaxValue )
1498  {
1499  dtMaxValue = array[i];
1500  }
1501  }
1502 
1503  //values in distance transform are squared
1504  maxDistanceValue = std::sqrt( dtMaxValue );
1505  }
1506  else
1507  {
1508  //use max distance set in symbol properties
1509  maxDistanceValue = maxPixelDistance;
1510  }
1511 
1512  //update the pixels in the provided QImage
1513  int idx = 0;
1514  double squaredVal = 0;
1515  double pixVal = 0;
1516 
1517  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1518  {
1519  if ( context.renderingStopped() )
1520  break;
1521 
1522  QRgb *scanLine = reinterpret_cast< QRgb * >( im->scanLine( heightIndex ) );
1523  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1524  {
1525  //result of distance transform
1526  squaredVal = array[idx];
1527 
1528  //scale result to fit in the range [0, 1]
1529  if ( maxDistanceValue > 0 )
1530  {
1531  pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1532  }
1533  else
1534  {
1535  pixVal = 1.0;
1536  }
1537 
1538  //convert value to color from ramp
1539  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1540  scanLine[widthIndex] = qPremultiply( ramp->color( pixVal ).rgba() );
1541  idx++;
1542  }
1543  }
1544 }
1545 
1547 {
1548  QgsStringMap map;
1549  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1550  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
1551  map[QStringLiteral( "color_type" )] = QString::number( mColorType );
1552  map[QStringLiteral( "blur_radius" )] = QString::number( mBlurRadius );
1553  map[QStringLiteral( "use_whole_shape" )] = QString::number( mUseWholeShape );
1554  map[QStringLiteral( "max_distance" )] = QString::number( mMaxDistance );
1555  map[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
1556  map[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
1557  map[QStringLiteral( "ignore_rings" )] = QString::number( mIgnoreRings );
1558  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1559  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1560  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1561  if ( mGradientRamp )
1562  {
1563 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1564  map.unite( mGradientRamp->properties() );
1565 #else
1566  map.insert( mGradientRamp->properties() );
1567 #endif
1568  }
1569 
1570  return map;
1571 }
1572 
1574 {
1575  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1576  if ( mGradientRamp )
1577  {
1578  sl->setColorRamp( mGradientRamp->clone() );
1579  }
1580  sl->setDistanceUnit( mDistanceUnit );
1581  sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1582  sl->setIgnoreRings( mIgnoreRings );
1583  sl->setOffset( mOffset );
1584  sl->setOffsetUnit( mOffsetUnit );
1585  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1586  copyDataDefinedProperties( sl.get() );
1587  copyPaintEffect( sl.get() );
1588  return sl.release();
1589 }
1590 
1592 {
1593  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1594  return offsetBleed;
1595 }
1596 
1598 {
1599  mDistanceUnit = unit;
1600  mOffsetUnit = unit;
1601 }
1602 
1604 {
1605  if ( mDistanceUnit == mOffsetUnit )
1606  {
1607  return mDistanceUnit;
1608  }
1610 }
1611 
1613 {
1614  mDistanceMapUnitScale = scale;
1615  mOffsetMapUnitScale = scale;
1616 }
1617 
1619 {
1620  if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1621  {
1622  return mDistanceMapUnitScale;
1623  }
1624  return QgsMapUnitScale();
1625 }
1626 
1627 
1628 //QgsImageFillSymbolLayer
1629 
1631 {
1632  setSubSymbol( new QgsLineSymbol() );
1633 }
1634 
1635 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
1636 {
1637  QPainter *p = context.renderContext().painter();
1638  if ( !p )
1639  {
1640  return;
1641  }
1642 
1643  mNextAngle = mAngle;
1644  applyDataDefinedSettings( context );
1645 
1646  p->setPen( QPen( Qt::NoPen ) );
1647 
1648  QTransform bkTransform = mBrush.transform();
1649  if ( applyBrushTransformFromContext() && !context.renderContext().textureOrigin().isNull() )
1650  {
1651  QPointF leftCorner = context.renderContext().textureOrigin();
1652  QTransform t = mBrush.transform();
1653  t.translate( leftCorner.x(), leftCorner.y() );
1654  mBrush.setTransform( t );
1655  }
1656  else if ( context.renderContext().testFlag( QgsRenderContext::RenderMapTile ) )
1657  {
1658  //transform brush to upper left corner of geometry bbox
1659  QPointF leftCorner = points.boundingRect().topLeft();
1660  QTransform t = mBrush.transform();
1661  t.translate( leftCorner.x(), leftCorner.y() );
1662  mBrush.setTransform( t );
1663  }
1664 
1665  if ( context.selected() )
1666  {
1667  QColor selColor = context.renderContext().selectionColor();
1668  p->setBrush( QBrush( selColor ) );
1669  _renderPolygon( p, points, rings, context );
1670  }
1671 
1672  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1673  {
1674  QTransform t = mBrush.transform();
1675  t.rotate( mNextAngle );
1676  mBrush.setTransform( t );
1677  }
1678  p->setBrush( mBrush );
1679  _renderPolygon( p, points, rings, context );
1680  if ( mStroke )
1681  {
1682  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1683  if ( rings )
1684  {
1685  for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
1686  {
1687  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1688  }
1689  }
1690  }
1691 
1692  mBrush.setTransform( bkTransform );
1693 }
1694 
1696 {
1697  if ( !symbol ) //unset current stroke
1698  {
1699  mStroke.reset( nullptr );
1700  return true;
1701  }
1702 
1703  if ( symbol->type() != QgsSymbol::Line )
1704  {
1705  delete symbol;
1706  return false;
1707  }
1708 
1709  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
1710  if ( lineSymbol )
1711  {
1712  mStroke.reset( lineSymbol );
1713  return true;
1714  }
1715 
1716  delete symbol;
1717  return false;
1718 }
1719 
1721 {
1722  mStrokeWidthUnit = unit;
1723 }
1724 
1726 {
1727  return mStrokeWidthUnit;
1728 }
1729 
1731 {
1732  mStrokeWidthMapUnitScale = scale;
1733 }
1734 
1736 {
1737  return mStrokeWidthMapUnitScale;
1738 }
1739 
1741 {
1742  if ( mStroke && mStroke->symbolLayer( 0 ) )
1743  {
1744  double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1745  return subLayerBleed;
1746  }
1747  return 0;
1748 }
1749 
1751 {
1752  double width = mStrokeWidth;
1754  {
1757  }
1759 }
1760 
1762 {
1763  Q_UNUSED( context )
1764  if ( !mStroke )
1765  {
1766  return QColor( Qt::black );
1767  }
1768  return mStroke->color();
1769 }
1770 
1772 {
1773  return Qt::SolidLine;
1774 #if 0
1775  if ( !mStroke )
1776  {
1777  return Qt::SolidLine;
1778  }
1779  else
1780  {
1781  return mStroke->dxfPenStyle();
1782  }
1783 #endif //0
1784 }
1785 
1786 QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1787 {
1788  QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
1789  if ( mStroke )
1790  attr.unite( mStroke->usedAttributes( context ) );
1791  return attr;
1792 }
1793 
1795 {
1797  return true;
1798  if ( mStroke && mStroke->hasDataDefinedProperties() )
1799  return true;
1800  return false;
1801 }
1802 
1804 {
1805  return true;
1806 }
1807 
1808 
1809 //QgsSVGFillSymbolLayer
1810 
1811 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString &svgFilePath, double width, double angle )
1813  , mPatternWidth( width )
1814 {
1815  mStrokeWidth = 0.3;
1816  mAngle = angle;
1817  mColor = QColor( 255, 255, 255 );
1819 }
1820 
1821 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double width, double angle )
1823  , mPatternWidth( width )
1824  , mSvgData( svgData )
1825 {
1826  storeViewBox();
1827  mStrokeWidth = 0.3;
1828  mAngle = angle;
1829  mColor = QColor( 255, 255, 255 );
1830  setSubSymbol( new QgsLineSymbol() );
1831  setDefaultSvgParams();
1832 }
1833 
1835 {
1837  mPatternWidthUnit = unit;
1838  mSvgStrokeWidthUnit = unit;
1839  mStrokeWidthUnit = unit;
1840  mStroke->setOutputUnit( unit );
1841 }
1842 
1844 {
1846  if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit || mStrokeWidthUnit != unit )
1847  {
1849  }
1850  return unit;
1851 }
1852 
1854 {
1856  mPatternWidthMapUnitScale = scale;
1857  mSvgStrokeWidthMapUnitScale = scale;
1858  mStrokeWidthMapUnitScale = scale;
1859 }
1860 
1862 {
1863  if ( QgsImageFillSymbolLayer::mapUnitScale() == mPatternWidthMapUnitScale &&
1864  mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1865  mSvgStrokeWidthMapUnitScale == mStrokeWidthMapUnitScale )
1866  {
1867  return mPatternWidthMapUnitScale;
1868  }
1869  return QgsMapUnitScale();
1870 }
1871 
1872 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString &svgPath )
1873 {
1874  mSvgData = QgsApplication::svgCache()->getImageData( svgPath );
1875  storeViewBox();
1876 
1877  mSvgFilePath = svgPath;
1878  setDefaultSvgParams();
1879 }
1880 
1882 {
1883  QByteArray data;
1884  double width = 20;
1885  QString svgFilePath;
1886  double angle = 0.0;
1887 
1888  if ( properties.contains( QStringLiteral( "width" ) ) )
1889  {
1890  width = properties[QStringLiteral( "width" )].toDouble();
1891  }
1892  if ( properties.contains( QStringLiteral( "svgFile" ) ) )
1893  {
1894  svgFilePath = properties[QStringLiteral( "svgFile" )];
1895  }
1896  if ( properties.contains( QStringLiteral( "angle" ) ) )
1897  {
1898  angle = properties[QStringLiteral( "angle" )].toDouble();
1899  }
1900 
1901  std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1902  if ( !svgFilePath.isEmpty() )
1903  {
1904  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( svgFilePath, width, angle );
1905  }
1906  else
1907  {
1908  if ( properties.contains( QStringLiteral( "data" ) ) )
1909  {
1910  data = QByteArray::fromHex( properties[QStringLiteral( "data" )].toLocal8Bit() );
1911  }
1912  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( data, width, angle );
1913  }
1914 
1915  //svg parameters
1916  if ( properties.contains( QStringLiteral( "svgFillColor" ) ) )
1917  {
1918  //pre 2.5 projects used "svgFillColor"
1919  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgFillColor" )] ) );
1920  }
1921  else if ( properties.contains( QStringLiteral( "color" ) ) )
1922  {
1923  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
1924  }
1925  if ( properties.contains( QStringLiteral( "svgOutlineColor" ) ) )
1926  {
1927  //pre 2.5 projects used "svgOutlineColor"
1928  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgOutlineColor" )] ) );
1929  }
1930  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
1931  {
1932  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
1933  }
1934  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
1935  {
1936  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
1937  }
1938  if ( properties.contains( QStringLiteral( "svgOutlineWidth" ) ) )
1939  {
1940  //pre 2.5 projects used "svgOutlineWidth"
1941  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "svgOutlineWidth" )].toDouble() );
1942  }
1943  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
1944  {
1945  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
1946  }
1947  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
1948  {
1949  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
1950  }
1951 
1952  //units
1953  if ( properties.contains( QStringLiteral( "pattern_width_unit" ) ) )
1954  {
1955  symbolLayer->setPatternWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "pattern_width_unit" )] ) );
1956  }
1957  if ( properties.contains( QStringLiteral( "pattern_width_map_unit_scale" ) ) )
1958  {
1959  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "pattern_width_map_unit_scale" )] ) );
1960  }
1961  if ( properties.contains( QStringLiteral( "svg_outline_width_unit" ) ) )
1962  {
1963  symbolLayer->setSvgStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "svg_outline_width_unit" )] ) );
1964  }
1965  if ( properties.contains( QStringLiteral( "svg_outline_width_map_unit_scale" ) ) )
1966  {
1967  symbolLayer->setSvgStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "svg_outline_width_map_unit_scale" )] ) );
1968  }
1969  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
1970  {
1971  symbolLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
1972  }
1973  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1974  {
1975  symbolLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1976  }
1977 
1978  symbolLayer->restoreOldDataDefinedProperties( properties );
1979 
1980  return symbolLayer.release();
1981 }
1982 
1983 void QgsSVGFillSymbolLayer::resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving )
1984 {
1985  QgsStringMap::iterator it = properties.find( QStringLiteral( "svgFile" ) );
1986  if ( it != properties.end() )
1987  {
1988  if ( saving )
1989  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1990  else
1991  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1992  }
1993 }
1994 
1996 {
1997  return QStringLiteral( "SVGFill" );
1998 }
1999 
2000 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush, const QString &svgFilePath, double patternWidth, QgsUnitTypes::RenderUnit patternWidthUnit,
2001  const QColor &svgFillColor, const QColor &svgStrokeColor, double svgStrokeWidth,
2002  QgsUnitTypes::RenderUnit svgStrokeWidthUnit, const QgsSymbolRenderContext &context,
2003  const QgsMapUnitScale &patternWidthMapUnitScale, const QgsMapUnitScale &svgStrokeWidthMapUnitScale )
2004 {
2005  if ( mSvgViewBox.isNull() )
2006  {
2007  return;
2008  }
2009 
2011 
2012  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
2013  {
2014  brush.setTextureImage( QImage() );
2015  }
2016  else
2017  {
2018  bool fitsInCache = true;
2020  QImage patternImage = QgsApplication::svgCache()->svgAsImage( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
2021  context.renderContext().scaleFactor(), fitsInCache, 0, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
2022  if ( !fitsInCache )
2023  {
2024  QPicture patternPict = QgsApplication::svgCache()->svgAsPicture( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
2025  context.renderContext().scaleFactor(), false, 0, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
2026  double hwRatio = 1.0;
2027  if ( patternPict.width() > 0 )
2028  {
2029  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
2030  }
2031  patternImage = QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2032  patternImage.fill( 0 ); // transparent background
2033 
2034  QPainter p( &patternImage );
2035  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2036  }
2037 
2038  QTransform brushTransform;
2039  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2040  {
2041  QImage transparentImage = patternImage.copy();
2042  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2043  brush.setTextureImage( transparentImage );
2044  }
2045  else
2046  {
2047  brush.setTextureImage( patternImage );
2048  }
2049  brush.setTransform( brushTransform );
2050  }
2051 }
2052 
2054 {
2055 
2056  applyPattern( mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit, mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2057 
2058  if ( mStroke )
2059  {
2060  mStroke->startRender( context.renderContext(), context.fields() );
2061  }
2062 }
2063 
2065 {
2066  if ( mStroke )
2067  {
2068  mStroke->stopRender( context.renderContext() );
2069  }
2070 }
2071 
2073 {
2074  QgsStringMap map;
2075  if ( !mSvgFilePath.isEmpty() )
2076  {
2077  map.insert( QStringLiteral( "svgFile" ), mSvgFilePath );
2078  }
2079  else
2080  {
2081  map.insert( QStringLiteral( "data" ), QString( mSvgData.toHex() ) );
2082  }
2083 
2084  map.insert( QStringLiteral( "width" ), QString::number( mPatternWidth ) );
2085  map.insert( QStringLiteral( "angle" ), QString::number( mAngle ) );
2086 
2087  //svg parameters
2088  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2089  map.insert( QStringLiteral( "outline_color" ), QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2090  map.insert( QStringLiteral( "outline_width" ), QString::number( mSvgStrokeWidth ) );
2091 
2092  //units
2093  map.insert( QStringLiteral( "pattern_width_unit" ), QgsUnitTypes::encodeUnit( mPatternWidthUnit ) );
2094  map.insert( QStringLiteral( "pattern_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2095  map.insert( QStringLiteral( "svg_outline_width_unit" ), QgsUnitTypes::encodeUnit( mSvgStrokeWidthUnit ) );
2096  map.insert( QStringLiteral( "svg_outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSvgStrokeWidthMapUnitScale ) );
2097  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2098  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2099  return map;
2100 }
2101 
2103 {
2104  std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2105  if ( !mSvgFilePath.isEmpty() )
2106  {
2107  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth, mAngle );
2108  clonedLayer->setSvgFillColor( mColor );
2109  clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2110  clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2111  }
2112  else
2113  {
2114  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth, mAngle );
2115  }
2116 
2117  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2118  clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2119  clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2120  clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2121  clonedLayer->setStrokeWidthUnit( mStrokeWidthUnit );
2122  clonedLayer->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2123 
2124  if ( mStroke )
2125  {
2126  clonedLayer->setSubSymbol( mStroke->clone() );
2127  }
2128  copyDataDefinedProperties( clonedLayer.get() );
2129  copyPaintEffect( clonedLayer.get() );
2130  return clonedLayer.release();
2131 }
2132 
2133 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2134 {
2135  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2136  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2137  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2138  element.appendChild( symbolizerElem );
2139 
2140  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2141 
2142  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2143  symbolizerElem.appendChild( fillElem );
2144 
2145  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2146  fillElem.appendChild( graphicFillElem );
2147 
2148  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2149  graphicFillElem.appendChild( graphicElem );
2150 
2151  if ( !mSvgFilePath.isEmpty() )
2152  {
2153  // encode a parametric SVG reference
2154  double patternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2155  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mSvgStrokeWidth, mSvgStrokeWidthUnit, props );
2156  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mSvgFilePath, mColor, patternWidth, mSvgStrokeColor, strokeWidth );
2157  }
2158  else
2159  {
2160  // TODO: create svg from data
2161  // <se:InlineContent>
2162  symbolizerElem.appendChild( doc.createComment( QStringLiteral( "SVG from data not implemented yet" ) ) );
2163  }
2164 
2165  // <Rotation>
2166  QString angleFunc;
2167  bool ok;
2168  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2169  if ( !ok )
2170  {
2171  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2172  }
2173  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2174  {
2175  angleFunc = QString::number( angle + mAngle );
2176  }
2177  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2178 
2179  if ( mStroke )
2180  {
2181  // the stroke sub symbol should be stored within the Stroke element,
2182  // but it will be stored in a separated LineSymbolizer because it could
2183  // have more than one layer
2184  mStroke->toSld( doc, element, props );
2185  }
2186 }
2187 
2189 {
2190  QString path, mimeType;
2191  QColor fillColor, strokeColor;
2192  Qt::PenStyle penStyle;
2193  double size, strokeWidth;
2194 
2195  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2196  if ( fillElem.isNull() )
2197  return nullptr;
2198 
2199  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2200  if ( graphicFillElem.isNull() )
2201  return nullptr;
2202 
2203  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2204  if ( graphicElem.isNull() )
2205  return nullptr;
2206 
2207  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2208  return nullptr;
2209 
2210  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2211  return nullptr;
2212 
2213  QgsSymbolLayerUtils::lineFromSld( graphicElem, penStyle, strokeColor, strokeWidth );
2214 
2215  QString uom = element.attribute( QStringLiteral( "uom" ) );
2216  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2217  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
2218 
2219  double angle = 0.0;
2220  QString angleFunc;
2221  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2222  {
2223  bool ok;
2224  double d = angleFunc.toDouble( &ok );
2225  if ( ok )
2226  angle = d;
2227  }
2228 
2229  std::unique_ptr< QgsSVGFillSymbolLayer > sl = qgis::make_unique< QgsSVGFillSymbolLayer >( path, size, angle );
2230  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2231  sl->setSvgFillColor( fillColor );
2232  sl->setSvgStrokeColor( strokeColor );
2233  sl->setSvgStrokeWidth( strokeWidth );
2234 
2235  // try to get the stroke
2236  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2237  if ( !strokeElem.isNull() )
2238  {
2240  if ( l )
2241  {
2242  QgsSymbolLayerList layers;
2243  layers.append( l );
2244  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2245  }
2246  }
2247 
2248  return sl.release();
2249 }
2250 
2252 {
2256  {
2257  return; //no data defined settings
2258  }
2259 
2261  {
2262  context.setOriginalValueVariable( mAngle );
2264  }
2265 
2266  double width = mPatternWidth;
2268  {
2269  context.setOriginalValueVariable( mPatternWidth );
2271  }
2272  QString svgFile = mSvgFilePath;
2274  {
2275  context.setOriginalValueVariable( mSvgFilePath );
2277  context.renderContext().pathResolver() );
2278  }
2279  QColor svgFillColor = mColor;
2281  {
2284  }
2285  QColor svgStrokeColor = mSvgStrokeColor;
2287  {
2288  context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2290  }
2291  double strokeWidth = mSvgStrokeWidth;
2293  {
2294  context.setOriginalValueVariable( mSvgStrokeWidth );
2296  }
2297  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgStrokeColor, strokeWidth,
2298  mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2299 
2300 }
2301 
2302 void QgsSVGFillSymbolLayer::storeViewBox()
2303 {
2304  if ( !mSvgData.isEmpty() )
2305  {
2306  QSvgRenderer r( mSvgData );
2307  if ( r.isValid() )
2308  {
2309  mSvgViewBox = r.viewBoxF();
2310  return;
2311  }
2312  }
2313 
2314  mSvgViewBox = QRectF();
2315 }
2316 
2317 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2318 {
2319  if ( mSvgFilePath.isEmpty() )
2320  {
2321  return;
2322  }
2323 
2324  bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2325  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2326  QColor defaultFillColor, defaultStrokeColor;
2327  double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2328  QgsApplication::svgCache()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2329  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2330  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2331  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2332  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2333 
2334  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2335  double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2336 
2337  if ( hasDefaultFillColor )
2338  {
2339  mColor = defaultFillColor;
2340  mColor.setAlphaF( newFillOpacity );
2341  }
2342  if ( hasDefaultFillOpacity )
2343  {
2344  mColor.setAlphaF( defaultFillOpacity );
2345  }
2346  if ( hasDefaultStrokeColor )
2347  {
2348  mSvgStrokeColor = defaultStrokeColor;
2349  mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2350  }
2351  if ( hasDefaultStrokeOpacity )
2352  {
2353  mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2354  }
2355  if ( hasDefaultStrokeWidth )
2356  {
2357  mSvgStrokeWidth = defaultStrokeWidth;
2358  }
2359 }
2360 
2361 
2364 {
2365  setSubSymbol( new QgsLineSymbol() );
2366  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2367 }
2368 
2370 {
2371  mFillLineSymbol->setWidth( w );
2372  mLineWidth = w;
2373 }
2374 
2376 {
2377  mFillLineSymbol->setColor( c );
2378  mColor = c;
2379 }
2380 
2382 {
2383  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2384 }
2385 
2387 {
2388  delete mFillLineSymbol;
2389 }
2390 
2392 {
2393  if ( !symbol )
2394  {
2395  return false;
2396  }
2397 
2398  if ( symbol->type() == QgsSymbol::Line )
2399  {
2400  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
2401  if ( lineSymbol )
2402  {
2403  delete mFillLineSymbol;
2404  mFillLineSymbol = lineSymbol;
2405 
2406  return true;
2407  }
2408  }
2409  delete symbol;
2410  return false;
2411 }
2412 
2414 {
2415  return mFillLineSymbol;
2416 }
2417 
2419 {
2420  QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
2421  if ( mFillLineSymbol )
2422  attr.unite( mFillLineSymbol->usedAttributes( context ) );
2423  return attr;
2424 }
2425 
2427 {
2429  return true;
2430  if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2431  return true;
2432  return false;
2433 }
2434 
2436 {
2437  return 0;
2438 }
2439 
2441 {
2443  mDistanceUnit = unit;
2444  mLineWidthUnit = unit;
2445  mOffsetUnit = unit;
2446 }
2447 
2449 {
2451  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2452  {
2454  }
2455  return unit;
2456 }
2457 
2459 {
2461  mDistanceMapUnitScale = scale;
2462  mLineWidthMapUnitScale = scale;
2463  mOffsetMapUnitScale = scale;
2464 }
2465 
2467 {
2468  if ( QgsImageFillSymbolLayer::mapUnitScale() == mDistanceMapUnitScale &&
2469  mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2470  mLineWidthMapUnitScale == mOffsetMapUnitScale )
2471  {
2472  return mDistanceMapUnitScale;
2473  }
2474  return QgsMapUnitScale();
2475 }
2476 
2478 {
2479  std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2480 
2481  //default values
2482  double lineAngle = 45;
2483  double distance = 5;
2484  double lineWidth = 0.5;
2485  QColor color( Qt::black );
2486  double offset = 0.0;
2487 
2488  if ( properties.contains( QStringLiteral( "lineangle" ) ) )
2489  {
2490  //pre 2.5 projects used "lineangle"
2491  lineAngle = properties[QStringLiteral( "lineangle" )].toDouble();
2492  }
2493  else if ( properties.contains( QStringLiteral( "angle" ) ) )
2494  {
2495  lineAngle = properties[QStringLiteral( "angle" )].toDouble();
2496  }
2497  patternLayer->setLineAngle( lineAngle );
2498 
2499  if ( properties.contains( QStringLiteral( "distance" ) ) )
2500  {
2501  distance = properties[QStringLiteral( "distance" )].toDouble();
2502  }
2503  patternLayer->setDistance( distance );
2504 
2505  if ( properties.contains( QStringLiteral( "linewidth" ) ) )
2506  {
2507  //pre 2.5 projects used "linewidth"
2508  lineWidth = properties[QStringLiteral( "linewidth" )].toDouble();
2509  }
2510  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
2511  {
2512  lineWidth = properties[QStringLiteral( "outline_width" )].toDouble();
2513  }
2514  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
2515  {
2516  lineWidth = properties[QStringLiteral( "line_width" )].toDouble();
2517  }
2518  patternLayer->setLineWidth( lineWidth );
2519 
2520  if ( properties.contains( QStringLiteral( "color" ) ) )
2521  {
2522  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] );
2523  }
2524  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
2525  {
2526  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] );
2527  }
2528  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
2529  {
2530  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] );
2531  }
2532  patternLayer->setColor( color );
2533 
2534  if ( properties.contains( QStringLiteral( "offset" ) ) )
2535  {
2536  offset = properties[QStringLiteral( "offset" )].toDouble();
2537  }
2538  patternLayer->setOffset( offset );
2539 
2540 
2541  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
2542  {
2543  patternLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )] ) );
2544  }
2545  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
2546  {
2547  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )] ) );
2548  }
2549  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
2550  {
2551  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
2552  }
2553  else if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2554  {
2555  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2556  }
2557  if ( properties.contains( QStringLiteral( "line_width_map_unit_scale" ) ) )
2558  {
2559  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "line_width_map_unit_scale" )] ) );
2560  }
2561  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2562  {
2563  patternLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
2564  }
2565  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2566  {
2567  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
2568  }
2569  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2570  {
2571  patternLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2572  }
2573  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2574  {
2575  patternLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2576  }
2577 
2578  patternLayer->restoreOldDataDefinedProperties( properties );
2579 
2580  return patternLayer.release();
2581 }
2582 
2584 {
2585  return QStringLiteral( "LinePatternFill" );
2586 }
2587 
2588 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double lineAngle, double distance )
2589 {
2590  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2591 
2592  if ( !mFillLineSymbol )
2593  {
2594  return;
2595  }
2596  // We have to make a copy because marker intervals will have to be adjusted
2597  std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2598  if ( !fillLineSymbol )
2599  {
2600  return;
2601  }
2602 
2603  const QgsRenderContext &ctx = context.renderContext();
2604  //double strokePixelWidth = lineWidth * QgsSymbolLayerUtils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2605  double outputPixelDist = ctx.convertToPainterUnits( distance, mDistanceUnit, mDistanceMapUnitScale );
2606  double outputPixelOffset = ctx.convertToPainterUnits( mOffset, mOffsetUnit, mOffsetMapUnitScale );
2607 
2608  // NOTE: this may need to be modified if we ever change from a forced rasterized/brush approach,
2609  // because potentially we may want to allow vector based line pattern fills where the first line
2610  // is offset by a large distance
2611 
2612  // fix truncated pattern with larger offsets
2613  outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2614  if ( outputPixelOffset > outputPixelDist / 2.0 )
2615  outputPixelOffset -= outputPixelDist;
2616 
2617  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2618  // For marker lines we have to get markers interval.
2619  double outputPixelBleed = 0;
2620  double outputPixelInterval = 0; // maximum interval
2621  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2622  {
2623  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2624  double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() );
2625  outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2626 
2627  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2628  if ( markerLineLayer )
2629  {
2630  double outputPixelLayerInterval = ctx.convertToPainterUnits( markerLineLayer->interval(), markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2631 
2632  // There may be multiple marker lines with different intervals.
2633  // In theory we should find the least common multiple, but that could be too
2634  // big (multiplication of intervals in the worst case).
2635  // Because patterns without small common interval would look strange, we
2636  // believe that the longest interval should usually be sufficient.
2637  outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2638  }
2639  }
2640 
2641  if ( outputPixelInterval > 0 )
2642  {
2643  // We have to adjust marker intervals to integer pixel size to get
2644  // repeatable pattern.
2645  double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2646  outputPixelInterval = std::round( outputPixelInterval );
2647 
2648  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2649  {
2650  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2651 
2652  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2653  if ( markerLineLayer )
2654  {
2655  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2656  }
2657  }
2658  }
2659 
2660  //create image
2661  int height, width;
2662  lineAngle = std::fmod( lineAngle, 360 );
2663  if ( lineAngle < 0 )
2664  lineAngle += 360;
2665  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2666  {
2667  height = outputPixelDist;
2668  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2669  }
2670  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2671  {
2672  width = outputPixelDist;
2673  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2674  }
2675  else
2676  {
2677  height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2678  width = outputPixelDist / std::sin( lineAngle * M_PI / 180 );
2679 
2680  // recalculate real angle and distance after rounding to pixels
2681  lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2682  if ( lineAngle < 0 )
2683  {
2684  lineAngle += 360.;
2685  }
2686 
2687  height = std::abs( height );
2688  width = std::abs( width );
2689 
2690  outputPixelDist = std::abs( height * std::cos( lineAngle * M_PI / 180 ) );
2691 
2692  // Round offset to correspond to one pixel height, otherwise lines may
2693  // be shifted on tile border if offset falls close to pixel center
2694  int offsetHeight = static_cast< int >( std::round( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) );
2695  outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 );
2696  }
2697 
2698  //depending on the angle, we might need to render into a larger image and use a subset of it
2699  double dx = 0;
2700  double dy = 0;
2701 
2702  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2703  // thus we add integer multiplications of width and height covering the bleed
2704  int bufferMulti = static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2705 
2706  // Always buffer at least once so that center of line marker in upper right corner
2707  // does not fall outside due to representation error
2708  bufferMulti = std::max( bufferMulti, 1 );
2709 
2710  int xBuffer = width * bufferMulti;
2711  int yBuffer = height * bufferMulti;
2712  int innerWidth = width;
2713  int innerHeight = height;
2714  width += 2 * xBuffer;
2715  height += 2 * yBuffer;
2716 
2717  //protect from zero width/height image and symbol layer from eating too much memory
2718  if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2719  {
2720  return;
2721  }
2722 
2723  QImage patternImage( width, height, QImage::Format_ARGB32 );
2724  patternImage.fill( 0 );
2725 
2726  QPointF p1, p2, p3, p4, p5, p6;
2727  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2728  {
2729  p1 = QPointF( 0, yBuffer );
2730  p2 = QPointF( width, yBuffer );
2731  p3 = QPointF( 0, yBuffer + innerHeight );
2732  p4 = QPointF( width, yBuffer + innerHeight );
2733  }
2734  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2735  {
2736  p1 = QPointF( xBuffer, height );
2737  p2 = QPointF( xBuffer, 0 );
2738  p3 = QPointF( xBuffer + innerWidth, height );
2739  p4 = QPointF( xBuffer + innerWidth, 0 );
2740  }
2741  else if ( lineAngle > 0 && lineAngle < 90 )
2742  {
2743  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2744  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2745  p1 = QPointF( 0, height );
2746  p2 = QPointF( width, 0 );
2747  p3 = QPointF( -dx, height - dy );
2748  p4 = QPointF( width - dx, -dy );
2749  p5 = QPointF( dx, height + dy );
2750  p6 = QPointF( width + dx, dy );
2751  }
2752  else if ( lineAngle > 180 && lineAngle < 270 )
2753  {
2754  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2755  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2756  p1 = QPointF( width, 0 );
2757  p2 = QPointF( 0, height );
2758  p3 = QPointF( width - dx, -dy );
2759  p4 = QPointF( -dx, height - dy );
2760  p5 = QPointF( width + dx, dy );
2761  p6 = QPointF( dx, height + dy );
2762  }
2763  else if ( lineAngle > 90 && lineAngle < 180 )
2764  {
2765  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2766  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2767  p1 = QPointF( 0, 0 );
2768  p2 = QPointF( width, height );
2769  p5 = QPointF( dx, -dy );
2770  p6 = QPointF( width + dx, height - dy );
2771  p3 = QPointF( -dx, dy );
2772  p4 = QPointF( width - dx, height + dy );
2773  }
2774  else if ( lineAngle > 270 && lineAngle < 360 )
2775  {
2776  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2777  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2778  p1 = QPointF( width, height );
2779  p2 = QPointF( 0, 0 );
2780  p5 = QPointF( width + dx, height - dy );
2781  p6 = QPointF( dx, -dy );
2782  p3 = QPointF( width - dx, height + dy );
2783  p4 = QPointF( -dx, dy );
2784  }
2785 
2786  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2787  {
2788  QPointF tempPt;
2789  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2790  p3 = QPointF( tempPt.x(), tempPt.y() );
2791  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2792  p4 = QPointF( tempPt.x(), tempPt.y() );
2793  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2794  p5 = QPointF( tempPt.x(), tempPt.y() );
2795  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2796  p6 = QPointF( tempPt.x(), tempPt.y() );
2797 
2798  //update p1, p2 last
2799  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2800  p1 = QPointF( tempPt.x(), tempPt.y() );
2801  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2802  p2 = QPointF( tempPt.x(), tempPt.y() );
2803  }
2804 
2805  QPainter p( &patternImage );
2806 
2807 #if 0
2808  // DEBUG: Draw rectangle
2809  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2810  QPen pen( QColor( Qt::black ) );
2811  pen.setWidthF( 0.1 );
2812  pen.setCapStyle( Qt::FlatCap );
2813  p.setPen( pen );
2814 
2815  // To see this rectangle, comment buffer cut below.
2816  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2817  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2818  p.drawPolygon( polygon );
2819 
2820  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 );
2821  p.drawPolygon( polygon );
2822 #endif
2823 
2824  // Use antialiasing because without antialiasing lines are rendered to the
2825  // right and below the mathematically defined points (not symmetrical)
2826  // and such tiles become useless for are filling
2827  p.setRenderHint( QPainter::Antialiasing, true );
2828 
2829  // line rendering needs context for drawing on patternImage
2830  QgsRenderContext lineRenderContext;
2831  lineRenderContext.setPainter( &p );
2832  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
2834  lineRenderContext.setMapToPixel( mtp );
2835  lineRenderContext.setForceVectorOutput( false );
2836  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2837 
2838  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2839 
2840  QVector<QPolygonF> polygons;
2841  polygons.append( QPolygonF() << p1 << p2 );
2842  polygons.append( QPolygonF() << p3 << p4 );
2843  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2844  {
2845  polygons.append( QPolygonF() << p5 << p6 );
2846  }
2847 
2848  for ( const QPolygonF &polygon : qgis::as_const( polygons ) )
2849  {
2850  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2851  }
2852 
2853  fillLineSymbol->stopRender( lineRenderContext );
2854  p.end();
2855 
2856  // Cut off the buffer
2857  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2858 
2859  //set image to mBrush
2860  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2861  {
2862  QImage transparentImage = patternImage.copy();
2863  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2864  brush.setTextureImage( transparentImage );
2865  }
2866  else
2867  {
2868  brush.setTextureImage( patternImage );
2869  }
2870 
2871  QTransform brushTransform;
2872  brush.setTransform( brushTransform );
2873 }
2874 
2876 {
2877  applyPattern( context, mBrush, mLineAngle, mDistance );
2878 
2879  if ( mFillLineSymbol )
2880  {
2881  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2882  }
2883 }
2884 
2886 {
2887  if ( mFillLineSymbol )
2888  {
2889  mFillLineSymbol->stopRender( context.renderContext() );
2890  }
2891 }
2892 
2894 {
2895  QgsStringMap map;
2896  map.insert( QStringLiteral( "angle" ), QString::number( mLineAngle ) );
2897  map.insert( QStringLiteral( "distance" ), QString::number( mDistance ) );
2898  map.insert( QStringLiteral( "line_width" ), QString::number( mLineWidth ) );
2899  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2900  map.insert( QStringLiteral( "offset" ), QString::number( mOffset ) );
2901  map.insert( QStringLiteral( "distance_unit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
2902  map.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineWidthUnit ) );
2903  map.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
2904  map.insert( QStringLiteral( "distance_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2905  map.insert( QStringLiteral( "line_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2906  map.insert( QStringLiteral( "offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2907  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2908  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2909  return map;
2910 }
2911 
2913 {
2915  if ( mFillLineSymbol )
2916  {
2917  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2918  }
2919  copyPaintEffect( clonedLayer );
2920  copyDataDefinedProperties( clonedLayer );
2921  return clonedLayer;
2922 }
2923 
2924 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2925 {
2926  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2927  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2928  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2929  element.appendChild( symbolizerElem );
2930 
2931  // <Geometry>
2932  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2933 
2934  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2935  symbolizerElem.appendChild( fillElem );
2936 
2937  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2938  fillElem.appendChild( graphicFillElem );
2939 
2940  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2941  graphicFillElem.appendChild( graphicElem );
2942 
2943  //line properties must be inside the graphic definition
2944  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2945  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2946  lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2947  double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
2948  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "horline" ), QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2949 
2950  // <Rotation>
2951  QString angleFunc;
2952  bool ok;
2953  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2954  if ( !ok )
2955  {
2956  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mLineAngle );
2957  }
2958  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2959  {
2960  angleFunc = QString::number( angle + mLineAngle );
2961  }
2962  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2963 
2964  // <se:Displacement>
2965  QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2966  lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
2967  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
2968 }
2969 
2970 QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFactor ) const
2971 {
2972  QString featureStyle;
2973  featureStyle.append( "Brush(" );
2974  featureStyle.append( QStringLiteral( "fc:%1" ).arg( mColor.name() ) );
2975  featureStyle.append( QStringLiteral( ",bc:%1" ).arg( QLatin1String( "#00000000" ) ) ); //transparent background
2976  featureStyle.append( ",id:\"ogr-brush-2\"" );
2977  featureStyle.append( QStringLiteral( ",a:%1" ).arg( mLineAngle ) );
2978  featureStyle.append( QStringLiteral( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2979  featureStyle.append( ",dx:0mm" );
2980  featureStyle.append( QStringLiteral( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2981  featureStyle.append( ')' );
2982  return featureStyle;
2983 }
2984 
2986 {
2988  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2989  {
2990  return; //no data defined settings
2991  }
2992 
2993  double lineAngle = mLineAngle;
2995  {
2996  context.setOriginalValueVariable( mLineAngle );
2998  }
2999  double distance = mDistance;
3001  {
3002  context.setOriginalValueVariable( mDistance );
3004  }
3005  applyPattern( context, mBrush, lineAngle, distance );
3006 }
3007 
3009 {
3010  QString name;
3011  QColor fillColor, lineColor;
3012  double size, lineWidth;
3013  Qt::PenStyle lineStyle;
3014 
3015  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
3016  if ( fillElem.isNull() )
3017  return nullptr;
3018 
3019  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
3020  if ( graphicFillElem.isNull() )
3021  return nullptr;
3022 
3023  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
3024  if ( graphicElem.isNull() )
3025  return nullptr;
3026 
3027  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
3028  return nullptr;
3029 
3030  if ( name != QLatin1String( "horline" ) )
3031  return nullptr;
3032 
3033  double angle = 0.0;
3034  QString angleFunc;
3035  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3036  {
3037  bool ok;
3038  double d = angleFunc.toDouble( &ok );
3039  if ( ok )
3040  angle = d;
3041  }
3042 
3043  double offset = 0.0;
3044  QPointF vectOffset;
3045  if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) )
3046  {
3047  offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3048  }
3049 
3050  QString uom = element.attribute( QStringLiteral( "uom" ) );
3051  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3053 
3054  std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
3055  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3056  sl->setColor( lineColor );
3057  sl->setLineWidth( lineWidth );
3058  sl->setLineAngle( angle );
3059  sl->setOffset( offset );
3060  sl->setDistance( size );
3061 
3062  // try to get the stroke
3063  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
3064  if ( !strokeElem.isNull() )
3065  {
3067  if ( l )
3068  {
3069  QgsSymbolLayerList layers;
3070  layers.append( l );
3071  sl->setSubSymbol( new QgsLineSymbol( layers ) );
3072  }
3073  }
3074 
3075  return sl.release();
3076 }
3077 
3078 
3080 
3083 {
3084  mDistanceX = 15;
3085  mDistanceY = 15;
3086  mDisplacementX = 0;
3087  mDisplacementY = 0;
3088  mOffsetX = 0;
3089  mOffsetY = 0;
3090  setSubSymbol( new QgsMarkerSymbol() );
3091  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
3092 }
3093 
3095 {
3096  delete mMarkerSymbol;
3097 }
3098 
3100 {
3102  mDistanceXUnit = unit;
3103  mDistanceYUnit = unit;
3104  mDisplacementXUnit = unit;
3105  mDisplacementYUnit = unit;
3106  mOffsetXUnit = unit;
3107  mOffsetYUnit = unit;
3108  if ( mMarkerSymbol )
3109  {
3110  mMarkerSymbol->setOutputUnit( unit );
3111  }
3112 
3113 }
3114 
3116 {
3118  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit || mOffsetXUnit != unit || mOffsetYUnit != unit )
3119  {
3121  }
3122  return unit;
3123 }
3124 
3126 {
3128  mDistanceXMapUnitScale = scale;
3129  mDistanceYMapUnitScale = scale;
3132  mOffsetXMapUnitScale = scale;
3133  mOffsetYMapUnitScale = scale;
3134 }
3135 
3137 {
3144  {
3145  return mDistanceXMapUnitScale;
3146  }
3147  return QgsMapUnitScale();
3148 }
3149 
3151 {
3152  std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = qgis::make_unique< QgsPointPatternFillSymbolLayer >();
3153  if ( properties.contains( QStringLiteral( "distance_x" ) ) )
3154  {
3155  layer->setDistanceX( properties[QStringLiteral( "distance_x" )].toDouble() );
3156  }
3157  if ( properties.contains( QStringLiteral( "distance_y" ) ) )
3158  {
3159  layer->setDistanceY( properties[QStringLiteral( "distance_y" )].toDouble() );
3160  }
3161  if ( properties.contains( QStringLiteral( "displacement_x" ) ) )
3162  {
3163  layer->setDisplacementX( properties[QStringLiteral( "displacement_x" )].toDouble() );
3164  }
3165  if ( properties.contains( QStringLiteral( "displacement_y" ) ) )
3166  {
3167  layer->setDisplacementY( properties[QStringLiteral( "displacement_y" )].toDouble() );
3168  }
3169  if ( properties.contains( QStringLiteral( "offset_x" ) ) )
3170  {
3171  layer->setOffsetX( properties[QStringLiteral( "offset_x" )].toDouble() );
3172  }
3173  if ( properties.contains( QStringLiteral( "offset_y" ) ) )
3174  {
3175  layer->setOffsetY( properties[QStringLiteral( "offset_y" )].toDouble() );
3176  }
3177 
3178  if ( properties.contains( QStringLiteral( "distance_x_unit" ) ) )
3179  {
3180  layer->setDistanceXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_x_unit" )] ) );
3181  }
3182  if ( properties.contains( QStringLiteral( "distance_x_map_unit_scale" ) ) )
3183  {
3184  layer->setDistanceXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_x_map_unit_scale" )] ) );
3185  }
3186  if ( properties.contains( QStringLiteral( "distance_y_unit" ) ) )
3187  {
3188  layer->setDistanceYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_y_unit" )] ) );
3189  }
3190  if ( properties.contains( QStringLiteral( "distance_y_map_unit_scale" ) ) )
3191  {
3192  layer->setDistanceYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_y_map_unit_scale" )] ) );
3193  }
3194  if ( properties.contains( QStringLiteral( "displacement_x_unit" ) ) )
3195  {
3196  layer->setDisplacementXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_x_unit" )] ) );
3197  }
3198  if ( properties.contains( QStringLiteral( "displacement_x_map_unit_scale" ) ) )
3199  {
3200  layer->setDisplacementXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_x_map_unit_scale" )] ) );
3201  }
3202  if ( properties.contains( QStringLiteral( "displacement_y_unit" ) ) )
3203  {
3204  layer->setDisplacementYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_y_unit" )] ) );
3205  }
3206  if ( properties.contains( QStringLiteral( "displacement_y_map_unit_scale" ) ) )
3207  {
3208  layer->setDisplacementYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_y_map_unit_scale" )] ) );
3209  }
3210  if ( properties.contains( QStringLiteral( "offset_x_unit" ) ) )
3211  {
3212  layer->setOffsetXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_x_unit" )] ) );
3213  }
3214  if ( properties.contains( QStringLiteral( "offset_x_map_unit_scale" ) ) )
3215  {
3216  layer->setOffsetXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_x_map_unit_scale" )] ) );
3217  }
3218  if ( properties.contains( QStringLiteral( "offset_y_unit" ) ) )
3219  {
3220  layer->setOffsetYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_y_unit" )] ) );
3221  }
3222  if ( properties.contains( QStringLiteral( "offset_y_map_unit_scale" ) ) )
3223  {
3224  layer->setOffsetYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_y_map_unit_scale" )] ) );
3225  }
3226 
3227  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
3228  {
3229  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
3230  }
3231  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3232  {
3233  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3234  }
3235 
3236  layer->restoreOldDataDefinedProperties( properties );
3237 
3238  return layer.release();
3239 }
3240 
3242 {
3243  return QStringLiteral( "PointPatternFill" );
3244 }
3245 
3246 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double distanceX, double distanceY,
3247  double displacementX, double displacementY, double offsetX, double offsetY )
3248 {
3249  //render 3 rows and columns in one go to easily incorporate displacement
3250  const QgsRenderContext &ctx = context.renderContext();
3253 
3254  double widthOffset = std::fmod( ctx.convertToPainterUnits( offsetX, mOffsetXUnit, mOffsetXMapUnitScale ), width );
3255  double heightOffset = std::fmod( ctx.convertToPainterUnits( offsetY, mOffsetYUnit, mOffsetYMapUnitScale ), height );
3256 
3257  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3258  {
3259  QImage img;
3260  brush.setTextureImage( img );
3261  return;
3262  }
3263 
3264  QImage patternImage( width, height, QImage::Format_ARGB32 );
3265  patternImage.fill( 0 );
3266  if ( patternImage.isNull() )
3267  {
3268  brush.setTextureImage( QImage() );
3269  return;
3270  }
3271  if ( mMarkerSymbol )
3272  {
3273  QPainter p( &patternImage );
3274 
3275  //marker rendering needs context for drawing on patternImage
3276  QgsRenderContext pointRenderContext;
3277  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3278  pointRenderContext.setPainter( &p );
3279  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
3280 
3282  pointRenderContext.setFlag( QgsRenderContext::Antialiasing, true );
3284 
3287  pointRenderContext.setMapToPixel( mtp );
3288  pointRenderContext.setForceVectorOutput( false );
3289  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3290 
3291  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3292 
3293  //render points on distance grid
3294  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3295  {
3296  for ( double currentY = -height; currentY <= height * 2.0; currentY += height )
3297  {
3298  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.feature(), pointRenderContext );
3299  }
3300  }
3301 
3302  //render displaced points
3305  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3306  {
3307  for ( double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3308  {
3309  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.feature(), pointRenderContext );
3310  }
3311  }
3312 
3313  for ( double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3314  {
3315  for ( double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3316  {
3317  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3318  }
3319  }
3320 
3321  mMarkerSymbol->stopRender( pointRenderContext );
3322  }
3323 
3324  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
3325  {
3326  QImage transparentImage = patternImage.copy();
3327  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
3328  brush.setTextureImage( transparentImage );
3329  }
3330  else
3331  {
3332  brush.setTextureImage( patternImage );
3333  }
3334  QTransform brushTransform;
3335  brush.setTransform( brushTransform );
3336 }
3337 
3339 {
3340  // if we are using a vector based output, we need to render points as vectors
3341  // (OR if the marker has data defined symbology, in which case we need to evaluate this point-by-point)
3342  mRenderUsingMarkers = context.renderContext().forceVectorOutput() || mMarkerSymbol->hasDataDefinedProperties();
3343 
3344  if ( mRenderUsingMarkers )
3345  {
3346  mMarkerSymbol->startRender( context.renderContext() );
3347  }
3348  else
3349  {
3350  // optimised render for screen only, use image based brush
3352  }
3353 
3354  if ( mStroke )
3355  {
3356  mStroke->startRender( context.renderContext(), context.fields() );
3357  }
3358 }
3359 
3361 {
3362  if ( mRenderUsingMarkers )
3363  {
3364  mMarkerSymbol->stopRender( context.renderContext() );
3365  }
3366 
3367  if ( mStroke )
3368  {
3369  mStroke->stopRender( context.renderContext() );
3370  }
3371 }
3372 
3373 void QgsPointPatternFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
3374 {
3375  if ( !mRenderUsingMarkers )
3376  {
3377  // use image based brush for speed
3378  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3379  return;
3380  }
3381 
3382  // vector based output - so draw dot by dot!
3383  QPainter *p = context.renderContext().painter();
3384  if ( !p )
3385  {
3386  return;
3387  }
3388 
3389  double distanceX = mDistanceX;
3391  {
3394  }
3396 
3397  double distanceY = mDistanceY;
3399  {
3402  }
3404 
3405  double offsetX = mOffsetX;
3407  {
3410  }
3411  const double widthOffset = std::fmod( context.renderContext().convertToPainterUnits( offsetX, mOffsetXUnit, mOffsetXMapUnitScale ), width );
3412 
3413  double offsetY = mOffsetY;
3415  {
3418  }
3419  const double heightOffset = std::fmod( context.renderContext().convertToPainterUnits( offsetY, mOffsetYUnit, mOffsetYMapUnitScale ), height );
3420 
3421  double displacementX = mDisplacementX;
3423  {
3426  }
3427  const double displacementPixelX = context.renderContext().convertToPainterUnits( displacementX, mDisplacementXUnit, mDisplacementXMapUnitScale );
3428 
3429  double displacementY = mDisplacementY;
3431  {
3434  }
3435  const double displacementPixelY = context.renderContext().convertToPainterUnits( displacementY, mDisplacementYUnit, mDisplacementYMapUnitScale );
3436 
3437  p->setPen( QPen( Qt::NoPen ) );
3438 
3439  if ( context.selected() )
3440  {
3441  QColor selColor = context.renderContext().selectionColor();
3442  p->setBrush( QBrush( selColor ) );
3443  _renderPolygon( p, points, rings, context );
3444  }
3445 
3446  p->save();
3447 
3448  QPainterPath path;
3449  path.addPolygon( points );
3450  if ( rings )
3451  {
3452  for ( const QPolygonF &ring : *rings )
3453  {
3454  path.addPolygon( ring );
3455  }
3456  }
3457  p->setClipPath( path, Qt::IntersectClip );
3458 
3459  const double left = points.boundingRect().left();
3460  const double top = points.boundingRect().top();
3461  const double right = points.boundingRect().right();
3462  const double bottom = points.boundingRect().bottom();
3463 
3465  QgsExpressionContextScopePopper scopePopper( context.renderContext().expressionContext(), scope );
3466  int pointNum = 0;
3467  const bool needsExpressionContext = hasDataDefinedProperties();
3468 
3469  bool alternateColumn = false;
3470  int currentCol = -3; // because we actually render a few rows/cols outside the bounds, try to align the col/row numbers to start at 1 for the first visible row/col
3471  for ( double currentX = ( std::floor( left / width ) - 2 ) * width; currentX <= right + 2 * width; currentX += width, alternateColumn = !alternateColumn )
3472  {
3473  if ( needsExpressionContext )
3474  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_marker_column" ), ++currentCol, true ) );
3475 
3476  bool alternateRow = false;
3477  const double columnX = currentX + widthOffset;
3478  int currentRow = -3;
3479  for ( double currentY = ( std::floor( top / height ) - 2 ) * height; currentY <= bottom + 2 * height; currentY += height, alternateRow = !alternateRow )
3480  {
3481  double y = currentY + heightOffset;
3482  double x = columnX;
3483  if ( alternateRow )
3484  x += displacementPixelX;
3485 
3486  if ( !alternateColumn )
3487  y -= displacementPixelY;
3488 
3489  if ( needsExpressionContext )
3490  {
3492  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_marker_row" ), ++currentRow, true ) );
3493  }
3494 
3495  mMarkerSymbol->renderPoint( QPointF( x, y ), context.feature(), context.renderContext() );
3496  }
3497  }
3498 
3499  p->restore();
3500 
3501  if ( mStroke )
3502  {
3503  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
3504  if ( rings )
3505  {
3506  for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
3507  {
3508  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
3509  }
3510  }
3511  }
3512 }
3513 
3515 {
3516  QgsStringMap map;
3517  map.insert( QStringLiteral( "distance_x" ), QString::number( mDistanceX ) );
3518  map.insert( QStringLiteral( "distance_y" ), QString::number( mDistanceY ) );
3519  map.insert( QStringLiteral( "displacement_x" ), QString::number( mDisplacementX ) );
3520  map.insert( QStringLiteral( "displacement_y" ), QString::number( mDisplacementY ) );
3521  map.insert( QStringLiteral( "offset_x" ), QString::number( mOffsetX ) );
3522  map.insert( QStringLiteral( "offset_y" ), QString::number( mOffsetY ) );
3523  map.insert( QStringLiteral( "distance_x_unit" ), QgsUnitTypes::encodeUnit( mDistanceXUnit ) );
3524  map.insert( QStringLiteral( "distance_y_unit" ), QgsUnitTypes::encodeUnit( mDistanceYUnit ) );
3525  map.insert( QStringLiteral( "displacement_x_unit" ), QgsUnitTypes::encodeUnit( mDisplacementXUnit ) );
3526  map.insert( QStringLiteral( "displacement_y_unit" ), QgsUnitTypes::encodeUnit( mDisplacementYUnit ) );
3527  map.insert( QStringLiteral( "offset_x_unit" ), QgsUnitTypes::encodeUnit( mOffsetXUnit ) );
3528  map.insert( QStringLiteral( "offset_y_unit" ), QgsUnitTypes::encodeUnit( mOffsetYUnit ) );
3529  map.insert( QStringLiteral( "distance_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3530  map.insert( QStringLiteral( "distance_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3531  map.insert( QStringLiteral( "displacement_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3532  map.insert( QStringLiteral( "displacement_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3533  map.insert( QStringLiteral( "offset_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetXMapUnitScale ) );
3534  map.insert( QStringLiteral( "offset_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetYMapUnitScale ) );
3535  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
3536  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
3537  return map;
3538 }
3539 
3541 {
3543  if ( mMarkerSymbol )
3544  {
3545  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3546  }
3547  copyDataDefinedProperties( clonedLayer );
3548  copyPaintEffect( clonedLayer );
3549  return clonedLayer;
3550 }
3551 
3552 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3553 {
3554  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3555  {
3556  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
3557  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
3558  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
3559  element.appendChild( symbolizerElem );
3560 
3561  // <Geometry>
3562  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
3563 
3564  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
3565  symbolizerElem.appendChild( fillElem );
3566 
3567  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
3568  fillElem.appendChild( graphicFillElem );
3569 
3570  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3573  QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
3574  QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "distance" ), dist );
3575  symbolizerElem.appendChild( distanceElem );
3576 
3577  QgsSymbolLayer *layer = mMarkerSymbol->symbolLayer( i );
3578  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
3579  if ( !markerLayer )
3580  {
3581  QString errorMsg = QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3582  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3583  }
3584  else
3585  {
3586  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3587  }
3588  }
3589 }
3590 
3592 {
3593  Q_UNUSED( element )
3594  return nullptr;
3595 }
3596 
3598 {
3599  if ( !symbol )
3600  {
3601  return false;
3602  }
3603 
3604  if ( symbol->type() == QgsSymbol::Marker )
3605  {
3606  QgsMarkerSymbol *markerSymbol = static_cast<QgsMarkerSymbol *>( symbol );
3607  delete mMarkerSymbol;
3608  mMarkerSymbol = markerSymbol;
3609  }
3610  return true;
3611 }
3612 
3614 {
3619  {
3620  return;
3621  }
3622 
3623  double distanceX = mDistanceX;
3625  {
3628  }
3629  double distanceY = mDistanceY;
3631  {
3634  }
3635  double displacementX = mDisplacementX;
3637  {
3640  }
3641  double displacementY = mDisplacementY;
3643  {
3646  }
3647  double offsetX = mOffsetX;
3649  {
3652  }
3653  double offsetY = mOffsetY;
3655  {
3658  }
3659  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY, offsetX, offsetY );
3660 }
3661 
3663 {
3664  return 0;
3665 }
3666 
3668 {
3669  QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes( context );
3670 
3671  if ( mMarkerSymbol )
3672  attributes.unite( mMarkerSymbol->usedAttributes( context ) );
3673 
3674  return attributes;
3675 }
3676 
3678 {
3680  return true;
3682  return true;
3683  return false;
3684 }
3685 
3687 {
3688  mColor = c;
3689  if ( mMarkerSymbol )
3690  mMarkerSymbol->setColor( c );
3691 }
3692 
3694 {
3695  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3696 }
3697 
3699 
3700 
3702 {
3703  setSubSymbol( new QgsMarkerSymbol() );
3704 }
3705 
3707 {
3708  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3709 
3710  if ( properties.contains( QStringLiteral( "point_on_surface" ) ) )
3711  sl->setPointOnSurface( properties[QStringLiteral( "point_on_surface" )].toInt() != 0 );
3712  if ( properties.contains( QStringLiteral( "point_on_all_parts" ) ) )
3713  sl->setPointOnAllParts( properties[QStringLiteral( "point_on_all_parts" )].toInt() != 0 );
3714  if ( properties.contains( QStringLiteral( "clip_points" ) ) )
3715  sl->setClipPoints( properties[QStringLiteral( "clip_points" )].toInt() != 0 );
3716  if ( properties.contains( QStringLiteral( "clip_on_current_part_only" ) ) )
3717  sl->setClipOnCurrentPartOnly( properties[QStringLiteral( "clip_on_current_part_only" )].toInt() != 0 );
3718 
3719  sl->restoreOldDataDefinedProperties( properties );
3720 
3721  return sl.release();
3722 }
3723 
3725 {
3726  return QStringLiteral( "CentroidFill" );
3727 }
3728 
3729 void QgsCentroidFillSymbolLayer::setColor( const QColor &color )
3730 {
3731  mMarker->setColor( color );
3732  mColor = color;
3733 }
3734 
3736 {
3737  return mMarker ? mMarker->color() : mColor;
3738 }
3739 
3741 {
3742  mMarker->setOpacity( context.opacity() );
3743  mMarker->startRender( context.renderContext(), context.fields() );
3744 
3745  mCurrentFeatureId = -1;
3746  mBiggestPartIndex = 0;
3747 }
3748 
3750 {
3751  mMarker->stopRender( context.renderContext() );
3752 }
3753 
3754 void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
3755 {
3756  Part part;
3757  part.exterior = points;
3758  if ( rings )
3759  part.rings = *rings;
3760 
3761  if ( mRenderingFeature )
3762  {
3763  // in the middle of rendering a possibly multi-part feature, so we collect all the parts and defer the actual rendering
3764  // until after we've received the final part
3765  mCurrentParts << part;
3766  }
3767  else
3768  {
3769  // not rendering a feature, so we can just render the polygon immediately
3770  render( context.renderContext(), QVector<Part>() << part, context.feature() ? *context.feature() : QgsFeature(), context.selected() );
3771  }
3772 }
3773 
3775 {
3776  mRenderingFeature = true;
3777  mCurrentParts.clear();
3778 }
3779 
3781 {
3782  mRenderingFeature = false;
3783  render( context, mCurrentParts, feature, false );
3784 }
3785 
3786 void QgsCentroidFillSymbolLayer::render( QgsRenderContext &context, const QVector<QgsCentroidFillSymbolLayer::Part> &parts, const QgsFeature &feature, bool selected )
3787 {
3790  bool clipPoints = mClipPoints;
3792 
3793  // TODO add expressions support
3794 
3795  QVector< QgsGeometry > geometryParts;
3796  geometryParts.reserve( parts.size() );
3797  QPainterPath globalPath;
3798 
3799  int maxArea = 0;
3800  int maxAreaPartIdx = 0;
3801 
3802  for ( int i = 0; i < parts.size(); i++ )
3803  {
3804  const Part part = parts[i];
3805  QgsGeometry geom = QgsGeometry::fromQPolygonF( part.exterior );
3806 
3807  if ( !geom.isNull() && !part.rings.empty() )
3808  {
3809  QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.get() );
3810 
3811  if ( !pointOnAllParts )
3812  {
3813  int area = poly->area();
3814 
3815  if ( area > maxArea )
3816  {
3817  maxArea = area;
3818  maxAreaPartIdx = i;
3819  }
3820  }
3821  }
3822 
3824  {
3825  globalPath.addPolygon( part.exterior );
3826  for ( const QPolygonF &ring : part.rings )
3827  {
3828  globalPath.addPolygon( ring );
3829  }
3830  }
3831  }
3832 
3833  for ( int i = 0; i < parts.size(); i++ )
3834  {
3835  if ( !pointOnAllParts && i != maxAreaPartIdx )
3836  continue;
3837 
3838  const Part part = parts[i];
3839 
3840  if ( clipPoints )
3841  {
3842  QPainterPath path;
3843 
3844  if ( clipOnCurrentPartOnly )
3845  {
3846  path.addPolygon( part.exterior );
3847  for ( const QPolygonF &ring : part.rings )
3848  {
3849  path.addPolygon( ring );
3850  }
3851  }
3852  else
3853  {
3854  path = globalPath;
3855  }
3856 
3857  context.painter()->save();
3858  context.painter()->setClipPath( path );
3859  }
3860 
3861  QPointF centroid = pointOnSurface ? QgsSymbolLayerUtils::polygonPointOnSurface( part.exterior, &part.rings ) : QgsSymbolLayerUtils::polygonCentroid( part.exterior );
3862  mMarker->renderPoint( centroid, feature.isValid() ? &feature : nullptr, context, -1, selected );
3863 
3864  if ( clipPoints )
3865  {
3866  context.painter()->restore();
3867  }
3868  }
3869 }
3870 
3872 {
3873  QgsStringMap map;
3874  map[QStringLiteral( "point_on_surface" )] = QString::number( mPointOnSurface );
3875  map[QStringLiteral( "point_on_all_parts" )] = QString::number( mPointOnAllParts );
3876  map[QStringLiteral( "clip_points" )] = QString::number( mClipPoints );
3877  map[QStringLiteral( "clip_on_current_part_only" )] = QString::number( mClipOnCurrentPartOnly );
3878  return map;
3879 }
3880 
3882 {
3883  std::unique_ptr< QgsCentroidFillSymbolLayer > x = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3884  x->mAngle = mAngle;
3885  x->mColor = mColor;
3886  x->setSubSymbol( mMarker->clone() );
3887  x->setPointOnSurface( mPointOnSurface );
3888  x->setPointOnAllParts( mPointOnAllParts );
3889  x->setClipPoints( mClipPoints );
3890  x->setClipOnCurrentPartOnly( mClipOnCurrentPartOnly );
3891  copyDataDefinedProperties( x.get() );
3892  copyPaintEffect( x.get() );
3893  return x.release();
3894 }
3895 
3896 void QgsCentroidFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3897 {
3898  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3899  // used with PointSymbolizer, then the semantic is to use the centroid
3900  // of the geometry, or any similar representative point.
3901  mMarker->toSld( doc, element, props );
3902 }
3903 
3905 {
3907  if ( !l )
3908  return nullptr;
3909 
3910  QgsSymbolLayerList layers;
3911  layers.append( l );
3912  std::unique_ptr< QgsMarkerSymbol > marker( new QgsMarkerSymbol( layers ) );
3913 
3914  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3915  sl->setSubSymbol( marker.release() );
3916  sl->setPointOnAllParts( false );
3917  return sl.release();
3918 }
3919 
3920 
3922 {
3923  return mMarker.get();
3924 }
3925 
3927 {
3928  if ( !symbol || symbol->type() != QgsSymbol::Marker )
3929  {
3930  delete symbol;
3931  return false;
3932  }
3933 
3934  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
3935  mColor = mMarker->color();
3936  return true;
3937 }
3938 
3940 {
3941  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
3942 
3943  if ( mMarker )
3944  attributes.unite( mMarker->usedAttributes( context ) );
3945 
3946  return attributes;
3947 }
3948 
3950 {
3952  return true;
3953  if ( mMarker && mMarker->hasDataDefinedProperties() )
3954  return true;
3955  return false;
3956 }
3957 
3959 {
3960  if ( mMarker )
3961  {
3962  mMarker->setOutputUnit( unit );
3963  }
3964 }
3965 
3967 {
3968  if ( mMarker )
3969  {
3970  return mMarker->outputUnit();
3971  }
3972  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
3973 }
3974 
3976 {
3977  if ( mMarker )
3978  {
3979  mMarker->setMapUnitScale( scale );
3980  }
3981 }
3982 
3984 {
3985  if ( mMarker )
3986  {
3987  return mMarker->mapUnitScale();
3988  }
3989  return QgsMapUnitScale();
3990 }
3991 
3992 
3993 
3994 
3997  , mImageFilePath( imageFilePath )
3998 {
3999  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
4000 }
4001 
4003 {
4005  double alpha = 1.0;
4006  QPointF offset;
4007  double angle = 0.0;
4008  double width = 0.0;
4009 
4010  QString imagePath;
4011  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4012  {
4013  imagePath = properties[QStringLiteral( "imageFile" )];
4014  }
4015  if ( properties.contains( QStringLiteral( "coordinate_mode" ) ) )
4016  {
4017  mode = static_cast< FillCoordinateMode >( properties[QStringLiteral( "coordinate_mode" )].toInt() );
4018  }
4019  if ( properties.contains( QStringLiteral( "alpha" ) ) )
4020  {
4021  alpha = properties[QStringLiteral( "alpha" )].toDouble();
4022  }
4023  if ( properties.contains( QStringLiteral( "offset" ) ) )
4024  {
4025  offset = QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] );
4026  }
4027  if ( properties.contains( QStringLiteral( "angle" ) ) )
4028  {
4029  angle = properties[QStringLiteral( "angle" )].toDouble();
4030  }
4031  if ( properties.contains( QStringLiteral( "width" ) ) )
4032  {
4033  width = properties[QStringLiteral( "width" )].toDouble();
4034  }
4035  std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = qgis::make_unique< QgsRasterFillSymbolLayer >( imagePath );
4036  symbolLayer->setCoordinateMode( mode );
4037  symbolLayer->setOpacity( alpha );
4038  symbolLayer->setOffset( offset );
4039  symbolLayer->setAngle( angle );
4040  symbolLayer->setWidth( width );
4041  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
4042  {
4043  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
4044  }
4045  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
4046  {
4047  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
4048  }
4049  if ( properties.contains( QStringLiteral( "width_unit" ) ) )
4050  {
4051  symbolLayer->setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "width_unit" )] ) );
4052  }
4053  if ( properties.contains( QStringLiteral( "width_map_unit_scale" ) ) )
4054  {
4055  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "width_map_unit_scale" )] ) );
4056  }
4057 
4058  symbolLayer->restoreOldDataDefinedProperties( properties );
4059 
4060  return symbolLayer.release();
4061 }
4062 
4063 void QgsRasterFillSymbolLayer::resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving )
4064 {
4065  QgsStringMap::iterator it = properties.find( QStringLiteral( "imageFile" ) );
4066  if ( it != properties.end() )
4067  {
4068  if ( saving )
4069  it.value() = pathResolver.writePath( it.value() );
4070  else
4071  it.value() = pathResolver.readPath( it.value() );
4072  }
4073 }
4074 
4076 {
4077  Q_UNUSED( symbol )
4078  return true;
4079 }
4080 
4082 {
4083  return QStringLiteral( "RasterFill" );
4084 }
4085 
4086 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
4087 {
4088  QPainter *p = context.renderContext().painter();
4089  if ( !p )
4090  {
4091  return;
4092  }
4093 
4094  QPointF offset = mOffset;
4096  {
4098  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
4099  bool ok = false;
4100  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
4101  if ( ok )
4102  offset = res;
4103  }
4104  if ( !offset.isNull() )
4105  {
4106  offset.setX( context.renderContext().convertToPainterUnits( offset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
4107  offset.setY( context.renderContext().convertToPainterUnits( offset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
4108  p->translate( offset );
4109  }
4110  if ( mCoordinateMode == Feature )
4111  {
4112  QRectF boundingRect = points.boundingRect();
4113  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
4114  boundingRect.top() - mBrush.transform().dy() ) );
4115  }
4116 
4117  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
4118  if ( !offset.isNull() )
4119  {
4120  p->translate( -offset );
4121  }
4122 }
4123 
4125 {
4126  applyPattern( mBrush, mImageFilePath, mWidth, mOpacity, context );
4127 }
4128 
4130 {
4131  Q_UNUSED( context )
4132 }
4133 
4135 {
4136  QgsStringMap map;
4137  map[QStringLiteral( "imageFile" )] = mImageFilePath;
4138  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
4139  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
4140  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
4141  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4142  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4143  map[QStringLiteral( "angle" )] = QString::number( mAngle );
4144  map[QStringLiteral( "width" )] = QString::number( mWidth );
4145  map[QStringLiteral( "width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
4146  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
4147  return map;
4148 }
4149 
4151 {
4152  std::unique_ptr< QgsRasterFillSymbolLayer > sl = qgis::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
4153  sl->setCoordinateMode( mCoordinateMode );
4154  sl->setOpacity( mOpacity );
4155  sl->setOffset( mOffset );
4156  sl->setOffsetUnit( mOffsetUnit );
4157  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
4158  sl->setAngle( mAngle );
4159  sl->setWidth( mWidth );
4160  sl->setWidthUnit( mWidthUnit );
4161  sl->setWidthMapUnitScale( mWidthMapUnitScale );
4162  copyDataDefinedProperties( sl.get() );
4163  copyPaintEffect( sl.get() );
4164  return sl.release();
4165 }
4166 
4168 {
4169  return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
4170 }
4171 
4172 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
4173 {
4174  mImageFilePath = imagePath;
4175 }
4176 
4178 {
4179  mCoordinateMode = mode;
4180 }
4181 
4182 void QgsRasterFillSymbolLayer::setOpacity( const double opacity )
4183 {
4184  mOpacity = opacity;
4185 }
4186 
4188 {
4189  if ( !dataDefinedProperties().hasActiveProperties() )
4190  return; // shortcut
4191 
4192  bool hasWidthExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
4193  bool hasFileExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFile );
4194  bool hasOpacityExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity );
4195  bool hasAngleExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
4196 
4197  if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
4198  {
4199  return; //no data defined settings
4200  }
4201 
4202  bool ok;
4203  if ( hasAngleExpression )
4204  {
4205  context.setOriginalValueVariable( mAngle );
4207  if ( ok )
4208  mNextAngle = nextAngle;
4209  }
4210 
4211  if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
4212  {
4213  return; //nothing further to do
4214  }
4215 
4216  double width = mWidth;
4217  if ( hasWidthExpression )
4218  {
4219  context.setOriginalValueVariable( mWidth );
4221  }
4222  double opacity = mOpacity;
4223  if ( hasOpacityExpression )
4224  {
4225  context.setOriginalValueVariable( mOpacity );
4227  }
4228  QString file = mImageFilePath;
4229  if ( hasFileExpression )
4230  {
4231  context.setOriginalValueVariable( mImageFilePath );
4233  }
4234  applyPattern( mBrush, file, width, opacity, context );
4235 }
4236 
4238 {
4239  return false;
4240 }
4241 
4242 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolRenderContext &context )
4243 {
4244  QSize size;
4245  if ( width > 0 )
4246  {
4247  if ( mWidthUnit != QgsUnitTypes::RenderPercentage )
4248  {
4249  size.setWidth( context.renderContext().convertToPainterUnits( width, mWidthUnit, mWidthMapUnitScale ) );
4250  }
4251  else
4252  {
4253  // RenderPercentage Unit Type takes original image size
4255  if ( size.isEmpty() )
4256  return;
4257 
4258  size.setWidth( ( width * size.width() ) / 100.0 );
4259 
4260  // don't render symbols with size below one or above 10,000 pixels
4261  if ( static_cast< int >( size.width() ) < 1 || 10000.0 < size.width() )
4262  return;
4263  }
4264 
4265  size.setHeight( 0 );
4266  }
4267 
4268  bool cached;
4269  QImage img = QgsApplication::imageCache()->pathAsImage( imageFilePath, size, true, alpha, cached, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
4270  if ( img.isNull() )
4271  return;
4272 
4273  brush.setTextureImage( img );
4274 }
4275 
4276 
4277 //
4278 // QgsRandomMarkerFillSymbolLayer
4279 //
4280 
4281 QgsRandomMarkerFillSymbolLayer::QgsRandomMarkerFillSymbolLayer( int pointCount, CountMethod method, double densityArea, unsigned long seed )
4282  : mCountMethod( method )
4283  , mPointCount( pointCount )
4284  , mDensityArea( densityArea )
4285  , mSeed( seed )
4286 {
4287  setSubSymbol( new QgsMarkerSymbol() );
4288 }
4289 
4291 {
4292  const CountMethod countMethod = static_cast< CountMethod >( properties.value( QStringLiteral( "count_method" ), QStringLiteral( "0" ) ).toInt() );
4293  const int pointCount = properties.value( QStringLiteral( "point_count" ), QStringLiteral( "10" ) ).toInt();
4294  const double densityArea = properties.value( QStringLiteral( "density_area" ), QStringLiteral( "250.0" ) ).toDouble();
4295 
4296  unsigned long seed = 0;
4297  if ( properties.contains( QStringLiteral( "seed" ) ) )
4298  seed = properties.value( QStringLiteral( "seed" ) ).toULong();
4299  else
4300  {
4301  // if we a creating a new random marker fill from scratch, we default to a random seed
4302  // because seed based fills are just nicer for users vs seeing points jump around with every map refresh
4303  std::random_device rd;
4304  std::mt19937 mt( seed == 0 ? rd() : seed );
4305  std::uniform_int_distribution<> uniformDist( 1, 999999999 );
4306  seed = uniformDist( mt );
4307  }
4308 
4309  std::unique_ptr< QgsRandomMarkerFillSymbolLayer > sl = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( pointCount, countMethod, densityArea, seed );
4310 
4311  if ( properties.contains( QStringLiteral( "density_area_unit" ) ) )
4312  sl->setDensityAreaUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "density_area_unit" )] ) );
4313  if ( properties.contains( QStringLiteral( "density_area_unit_scale" ) ) )
4314  sl->setDensityAreaUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "density_area_unit_scale" )] ) );
4315 
4316  if ( properties.contains( QStringLiteral( "clip_points" ) ) )
4317  {
4318  sl->setClipPoints( properties[QStringLiteral( "clip_points" )].toInt() );
4319  }
4320 
4321  return sl.release();
4322 }
4323 
4325 {
4326  return QStringLiteral( "RandomMarkerFill" );
4327 }
4328 
4329 void QgsRandomMarkerFillSymbolLayer::setColor( const QColor &color )
4330 {
4331  mMarker->setColor( color );
4332  mColor = color;
4333 }
4334 
4336 {
4337  return mMarker ? mMarker->color() : mColor;
4338 }
4339 
4341 {
4342  mMarker->setOpacity( context.opacity() );
4343  mMarker->startRender( context.renderContext(), context.fields() );
4344 }
4345 
4347 {
4348  mMarker->stopRender( context.renderContext() );
4349 }
4350 
4351 void QgsRandomMarkerFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
4352 {
4353  Part part;
4354  part.exterior = points;
4355  if ( rings )
4356  part.rings = *rings;
4357 
4358  if ( mRenderingFeature )
4359  {
4360  // in the middle of rendering a possibly multi-part feature, so we collect all the parts and defer the actual rendering
4361  // until after we've received the final part
4362  mCurrentParts << part;
4363  }
4364  else
4365  {
4366  // not rendering a feature, so we can just render the polygon immediately
4367  render( context.renderContext(), QVector< Part>() << part, context.feature() ? *context.feature() : QgsFeature(), context.selected() );
4368  }
4369 }
4370 
4371 void QgsRandomMarkerFillSymbolLayer::render( QgsRenderContext &context, const QVector<QgsRandomMarkerFillSymbolLayer::Part> &parts, const QgsFeature &feature, bool selected )
4372 {
4373  bool clipPoints = mClipPoints;
4375  {
4378  }
4379 
4380  QVector< QgsGeometry > geometryParts;
4381  geometryParts.reserve( parts.size() );
4382  QPainterPath path;
4383 
4384  for ( const Part &part : parts )
4385  {
4386  QgsGeometry geom = QgsGeometry::fromQPolygonF( part.exterior );
4387  if ( !geom.isNull() && !part.rings.empty() )
4388  {
4389  QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.get() );
4390  for ( const QPolygonF &ring : part.rings )
4391  {
4393  }
4394  }
4395  if ( !geom.isGeosValid() )
4396  {
4397  geom = geom.buffer( 0, 0 );
4398  }
4399  geometryParts << geom;
4400 
4401  if ( clipPoints )
4402  {
4403  path.addPolygon( part.exterior );
4404  for ( const QPolygonF &ring : part.rings )
4405  {
4406  path.addPolygon( ring );
4407  }
4408  }
4409  }
4410 
4411  const QgsGeometry geom = geometryParts.count() != 1 ? QgsGeometry::unaryUnion( geometryParts ) : geometryParts.at( 0 );
4412 
4413  if ( clipPoints )
4414  {
4415  context.painter()->save();
4416  context.painter()->setClipPath( path );
4417  }
4418 
4419 
4420  int count = mPointCount;
4422  {
4423  context.expressionContext().setOriginalValueVariable( count );
4425  }
4426 
4427  switch ( mCountMethod )
4428  {
4429  case DensityBasedCount:
4430  {
4431  double densityArea = mDensityArea;
4433  {
4436  }
4437  densityArea = context.convertToPainterUnits( std::sqrt( densityArea ), mDensityAreaUnit, mDensityAreaUnitScale );
4438  densityArea = std::pow( densityArea, 2 );
4439  count = std::max( 0.0, std::ceil( count * ( geom.area() / densityArea ) ) );
4440  break;
4441  }
4442  case AbsoluteCount:
4443  break;
4444  }
4445 
4446  unsigned long seed = mSeed;
4448  {
4449  context.expressionContext().setOriginalValueVariable( static_cast< unsigned long long >( seed ) );
4451  }
4452 
4453  QVector< QgsPointXY > randomPoints = geom.randomPointsInPolygon( count, seed );
4454 #if 0
4455  // in some cases rendering from top to bottom is nice (e.g. randomised tree markers), but in other cases it's not wanted..
4456  // TODO consider exposing this as an option
4457  std::sort( randomPoints.begin(), randomPoints.end(), []( const QgsPointXY & a, const QgsPointXY & b )->bool
4458  {
4459  return a.y() < b.y();
4460  } );
4461 #endif
4463  QgsExpressionContextScopePopper scopePopper( context.expressionContext(), scope );
4464  int pointNum = 0;
4465  const bool needsExpressionContext = hasDataDefinedProperties();
4466 
4467  for ( const QgsPointXY &p : qgis::as_const( randomPoints ) )
4468  {
4469  if ( needsExpressionContext )
4471  mMarker->renderPoint( QPointF( p.x(), p.y() ), feature.isValid() ? &feature : nullptr, context, -1, selected );
4472  }
4473 
4474  if ( clipPoints )
4475  {
4476  context.painter()->restore();
4477  }
4478 }
4479 
4481 {
4482  QgsStringMap map;
4483  map.insert( QStringLiteral( "count_method" ), QString::number( static_cast< int >( mCountMethod ) ) );
4484  map.insert( QStringLiteral( "point_count" ), QString::number( mPointCount ) );
4485  map.insert( QStringLiteral( "density_area" ), QString::number( mDensityArea ) );
4486  map.insert( QStringLiteral( "density_area_unit" ), QgsUnitTypes::encodeUnit( mDensityAreaUnit ) );
4487  map.insert( QStringLiteral( "density_area_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDensityAreaUnitScale ) );
4488  map.insert( QStringLiteral( "seed" ), QString::number( mSeed ) );
4489  map.insert( QStringLiteral( "clip_points" ), QString::number( mClipPoints ) );
4490  return map;
4491 }
4492 
4494 {
4495  std::unique_ptr< QgsRandomMarkerFillSymbolLayer > res = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( mPointCount, mCountMethod, mDensityArea, mSeed );
4496  res->mAngle = mAngle;
4497  res->mColor = mColor;
4498  res->setDensityAreaUnit( mDensityAreaUnit );
4499  res->setDensityAreaUnitScale( mDensityAreaUnitScale );
4500  res->mClipPoints = mClipPoints;
4501  res->setSubSymbol( mMarker->clone() );
4502  copyDataDefinedProperties( res.get() );
4503  copyPaintEffect( res.get() );
4504  return res.release();
4505 }
4506 
4508 {
4509  return mMarker.get();
4510 }
4511 
4513 {
4514  if ( !symbol || symbol->type() != QgsSymbol::Marker )
4515  {
4516  delete symbol;
4517  return false;
4518  }
4519 
4520  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
4521  mColor = mMarker->color();
4522  return true;
4523 }
4524 
4526 {
4527  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
4528 
4529  if ( mMarker )
4530  attributes.unite( mMarker->usedAttributes( context ) );
4531 
4532  return attributes;
4533 }
4534 
4536 {
4538  return true;
4539  if ( mMarker && mMarker->hasDataDefinedProperties() )
4540  return true;
4541  return false;
4542 }
4543 
4545 {
4546  return mPointCount;
4547 }
4548 
4550 {
4551  mPointCount = pointCount;
4552 }
4553 
4555 {
4556  return mSeed;
4557 }
4558 
4560 {
4561  mSeed = seed;
4562 }
4563 
4565 {
4566  return mClipPoints;
4567 }
4568 
4570 {
4571  mClipPoints = clipPoints;
4572 }
4573 
4575 {
4576  return mCountMethod;
4577 }
4578 
4580 {
4581  mCountMethod = method;
4582 }
4583 
4585 {
4586  return mDensityArea;
4587 }
4588 
4590 {
4591  mDensityArea = area;
4592 }
4593 
4595 {
4596  mRenderingFeature = true;
4597  mCurrentParts.clear();
4598 }
4599 
4601 {
4602  mRenderingFeature = false;
4603  render( context, mCurrentParts, feature, false );
4604 }
4605 
4606 
4608 {
4609  if ( mMarker )
4610  {
4611  mMarker->setOutputUnit( unit );
4612  }
4613 }
4614 
4616 {
4617  if ( mMarker )
4618  {
4619  return mMarker->outputUnit();
4620  }
4621  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
4622 }
4623 
4625 {
4626  if ( mMarker )
4627  {
4628  mMarker->setMapUnitScale( scale );
4629  }
4630 }
4631 
4633 {
4634  if ( mMarker )
4635  {
4636  return mMarker->mapUnitScale();
4637  }
4638  return QgsMapUnitScale();
4639 }
4640 
QgsSVGFillSymbolLayer::stopRender
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Definition: qgsfillsymbollayer.cpp:2064
QgsCentroidFillSymbolLayer::pointOnAllParts
bool pointOnAllParts() const
Returns whether a point is drawn for all parts or only on the biggest part of multi-part features.
Definition: qgsfillsymbollayer.h:2060
qgspolygon.h
QgsLinePatternFillSymbolLayer::setMapUnitScale
void setMapUnitScale(const QgsMapUnitScale &scale) override
Definition: qgsfillsymbollayer.cpp:2458
QgsSimpleFillSymbolLayer::renderPolygon
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Renders the fill symbol layer for the polygon whose outer ring is defined by points,...
Definition: qgsfillsymbollayer.cpp:272
QgsSymbolRenderContext::setOriginalValueVariable
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1411
QgsSimpleFillSymbolLayer::strokeColor
QColor strokeColor() const override
Gets stroke color.
Definition: qgsfillsymbollayer.h:81
QgsGradientFillSymbolLayer::gradientType
GradientType gradientType() const
Type of gradient, e.g., linear or radial.
Definition: qgsfillsymbollayer.h:270
QgsPointPatternFillSymbolLayer
Definition: qgsfillsymbollayer.h:1540
QgsGradientFillSymbolLayer::layerType
QString layerType() const override
Returns a string that represents this layer type.
Definition: qgsfillsymbollayer.cpp:605
QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth
QString ogrFeatureStyleWidth(double widthScaleFactor) const
Definition: qgsfillsymbollayer.cpp:2970
QgsImageFillSymbolLayer::setSubSymbol
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
Definition: qgsfillsymbollayer.cpp:1695
QgsSimpleFillSymbolLayer::layerType
QString layerType() const override
Returns a string that represents this layer type.
Definition: qgsfillsymbollayer.cpp:237
QgsCentroidFillSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:3958
QgsColorRamp
Abstract base class for color ramps.
Definition: qgscolorramp.h:32
qgsexpressioncontextutils.h
QgsImageFillSymbolLayer::hasDataDefinedProperties
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Definition: qgsfillsymbollayer.cpp:1794
QgsPointPatternFillSymbolLayer::displacementY
double displacementY() const
Definition: qgsfillsymbollayer.h:1577
QgsSymbol::color
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:514
qgssvgcache.h
QgsSimpleFillSymbolLayer::createFromSld
static QgsSymbolLayer * createFromSld(QDomElement &element)
Definition: qgsfillsymbollayer.cpp:407
QgsSimpleFillSymbolLayer::create
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleFillSymbolLayer using the specified properties map containing symbol propertie...
Definition: qgsfillsymbollayer.cpp:150
QgsGradientFillSymbolLayer::mGradientColorType
GradientColorType mGradientColorType
Definition: qgsfillsymbollayer.h:380
QgsGradientFillSymbolLayer::QgsGradientFillSymbolLayer
QgsGradientFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, GradientColorType gradientColorType=SimpleTwoColor, GradientType gradientType=Linear, GradientCoordinateMode coordinateMode=Feature, GradientSpread gradientSpread=Pad)
Definition: qgsfillsymbollayer.cpp:496
QgsPointPatternFillSymbolLayer::mOffsetXMapUnitScale
QgsMapUnitScale mOffsetXMapUnitScale
Definition: qgsfillsymbollayer.h:1784
QgsPointPatternFillSymbolLayer::mDisplacementXMapUnitScale
QgsMapUnitScale mDisplacementXMapUnitScale
Definition: qgsfillsymbollayer.h:1778
QgsPointPatternFillSymbolLayer::mDistanceXUnit
QgsUnitTypes::RenderUnit mDistanceXUnit
Definition: qgsfillsymbollayer.h:1771
QgsRenderContext::testFlag
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Definition: qgsrendercontext.cpp:192
QgsExpressionContextScopePopper
RAII class to pop scope from an expression context on destruction.
Definition: qgsexpressioncontextutils.h:355
QgsLinePatternFillSymbolLayer::createFromSld
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsLinePatternFillSymbolLayer from a SLD element.
Definition: qgsfillsymbollayer.cpp:3008
QgsAbstractPropertyCollection::valueAsDouble
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.
Definition: qgspropertycollection.cpp:66
QgsRasterFillSymbolLayer::startRender
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
Definition: qgsfillsymbollayer.cpp:4124
QgsSymbolLayerUtils::encodeColor
static QString encodeColor(const QColor &color)
Definition: qgssymbollayerutils.cpp:52
QgsSVGFillSymbolLayer::startRender
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
Definition: qgsfillsymbollayer.cpp:2053
QgsSymbolLayer::PropertyBlurRadius
@ PropertyBlurRadius
Shapeburst blur radius.
Definition: qgssymbollayer.h:158
QgsGradientFillSymbolLayer::offset
QPointF offset() const
Returns the offset by which polygons will be translated during rendering.
Definition: qgsfillsymbollayer.h:340
QgsShapeburstFillSymbolLayer::properties
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Definition: qgsfillsymbollayer.cpp:1546
QgsSymbolLayer::PropertyLineDistance
@ PropertyLineDistance
Distance between lines, or length of lines for hash line symbols.
Definition: qgssymbollayer.h:148
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:318
QgsSymbolLayer::mColor
QColor mColor
Definition: qgssymbollayer.h:527
QgsRenderContext::setPainterFlagsUsingContext
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
Definition: qgsrendercontext.cpp:143
QgsSymbolLayer::PropertyShapeburstUseWholeShape
@ PropertyShapeburstUseWholeShape
Shapeburst use whole shape.
Definition: qgssymbollayer.h:159
QgsRandomMarkerFillSymbolLayer::clone
QgsRandomMarkerFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Definition: qgsfillsymbollayer.cpp:4493
QgsLinePatternFillSymbolLayer::layerType
QString layerType() const override
Returns a string that represents this layer type.
Definition: qgsfillsymbollayer.cpp:2583
QgsGradientFillSymbolLayer::setMapUnitScale
void setMapUnitScale(const QgsMapUnitScale &scale) override
Definition: qgsfillsymbollayer.cpp:982
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:325
QgsPointPatternFillSymbolLayer::mDisplacementXUnit
QgsUnitTypes::RenderUnit mDisplacementXUnit
Definition: qgsfillsymbollayer.h:1777
QgsSymbolLayerUtils::polygonPointOnSurface
static QPointF polygonPointOnSurface(const QPolygonF &points, const QVector< QPolygonF > *rings=nullptr)
Calculate a point on the surface of a QPolygonF.
Definition: qgssymbollayerutils.cpp:4074
QgsImageCache::originalSize
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
Definition: qgsimagecache.cpp:157
QgsSymbolLayer::dataDefinedProperties
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
Definition: qgssymbollayer.h:486
QgsCentroidFillSymbolLayer::stopRender
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Definition: qgsfillsymbollayer.cpp:3749
QgsGradientColorRamp
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:151
QgsSymbolLayer::PropertyClipPoints
@ PropertyClipPoints
Whether markers should be clipped to polygon boundaries.
Definition: qgssymbollayer.h:187
QgsImageFillSymbolLayer::QgsImageFillSymbolLayer
QgsImageFillSymbolLayer()
Definition: qgsfillsymbollayer.cpp:1630
QgsSymbolLayer::PropertyGradientReference2IsCentroid
@ PropertyGradientReference2IsCentroid
Gradient reference point 2 is centroid.
Definition: qgssymbollayer.h:157
QgsGradientFillSymbolLayer::mOffsetUnit
QgsUnitTypes::RenderUnit mOffsetUnit
Definition: qgsfillsymbollayer.h:393
QgsRasterFillSymbolLayer::opacity
double opacity() const
Returns the opacity for the raster image used in the fill.
Definition: qgsfillsymbollayer.h:884
QgsUnitTypes::RenderUnit
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
QgsDxfExport
Definition: qgsdxfexport.h:63
QgsRasterFillSymbolLayer::Feature
@ Feature
Tiling is based on feature bounding box.
Definition: qgsfillsymbollayer.h:806
QgsSimpleFillSymbolLayer::ogrFeatureStyle
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
Definition: qgsfillsymbollayer.cpp:396
QgsCentroidFillSymbolLayer::mPointOnSurface
bool mPointOnSurface
Definition: qgsfillsymbollayer.h:2100
QgsAbstractPropertyCollection::valueAsInt
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.
Definition: qgspropertycollection.cpp:77
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:596
QgsMapToPixel::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns current map units per pixel.
Definition: qgsmaptopixel.cpp:128
QgsSVGFillSymbolLayer::svgStrokeWidth
double svgStrokeWidth() const
Returns the stroke width used for rendering the SVG content.
Definition: qgsfillsymbollayer.h:1172
QgsSymbolLayerUtils::externalGraphicFromSld
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
Definition: qgssymbollayerutils.cpp:2299
QgsCptCityColorRamp::create
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Definition: qgscolorramp.cpp:628
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:34
qgslinestring.h
QgsSymbolLayerUtils::wellKnownMarkerFromSld
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
Definition: qgssymbollayerutils.cpp:2434
QgsRandomMarkerFillSymbolLayer::CountMethod
CountMethod
Methods to define the number of points randomly filling the polygon.
Definition: qgsfillsymbollayer.h:1816
QgsRandomMarkerFillSymbolLayer::stopFeatureRender
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
Definition: qgsfillsymbollayer.cpp:4600
QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer
QgsSVGFillSymbolLayer(const QString &svgFilePath, double width=20, double rotation=0.0)
Constructor for QgsSVGFillSymbolLayer, using the SVG picture at the specified absolute file path.
Definition: qgsfillsymbollayer.cpp:1811
QgsPointPatternFillSymbolLayer::stopRender
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Definition: qgsfillsymbollayer.cpp:3360
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:93
QgsLinePatternFillSymbolLayer::~QgsLinePatternFillSymbolLayer
~QgsLinePatternFillSymbolLayer() override
Definition: qgsfillsymbollayer.cpp:2386
QgsRandomMarkerFillSymbolLayer::subSymbol
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
Definition: qgsfillsymbollayer.cpp:4507
QgsGradientFillSymbolLayer::mReferencePoint2IsCentroid
bool mReferencePoint2IsCentroid
Definition: qgsfillsymbollayer.h:390
QgsGradientFillSymbolLayer::GradientType
GradientType
Definition: qgsfillsymbollayer.h:215
QgsSymbolLayerUtils::encodeMapUnitScale
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Definition: qgssymbollayerutils.cpp:558
QgsCentroidFillSymbolLayer::setSubSymbol
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
Definition: qgsfillsymbollayer.cpp:3926
QgsRandomMarkerFillSymbolLayer::usedAttributes
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Definition: qgsfillsymbollayer.cpp:4525
QgsImageFillSymbolLayer::dxfPenStyle
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
Definition: qgsfillsymbollayer.cpp:1771
QgsSimpleFillSymbolLayer::dxfPenStyle
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
Definition: qgsfillsymbollayer.cpp:474
QgsSymbolLayerUtils::sizeInPixelsFromSldUom
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
Definition: qgssymbollayerutils.cpp:4563
QgsRasterFillSymbolLayer::resolvePaths
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.
Definition: qgsfillsymbollayer.cpp:4063
QgsPointPatternFillSymbolLayer::mDistanceYMapUnitScale
QgsMapUnitScale mDistanceYMapUnitScale
Definition: qgsfillsymbollayer.h:1775
QgsSymbolRenderContext::feature
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:802
QgsGradientFillSymbolLayer
Definition: qgsfillsymbollayer.h:205
QgsShapeburstFillSymbolLayer::ShapeburstColorType
ShapeburstColorType
Definition: qgsfillsymbollayer.h:420
QgsSimpleFillSymbolLayer::stopRender
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Definition: qgsfillsymbollayer.cpp:267
qgsexpression.h
QgsGradientFillSymbolLayer::gradientColorType
GradientColorType gradientColorType() const
Gradient color mode, controls how gradient color stops are created.
Definition: qgsfillsymbollayer.h:274
QgsLinePatternFillSymbolLayer::setSubSymbol
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
Definition: qgsfillsymbollayer.cpp:2391
QgsSVGFillSymbolLayer::svgStrokeWidthUnit
QgsUnitTypes::RenderUnit svgStrokeWidthUnit() const
Returns the units for the stroke width.
Definition: qgsfillsymbollayer.h:1226
QgsCentroidFillSymbolLayer::setColor
void setColor(const QColor &color) override
The fill color.
Definition: qgsfillsymbollayer.cpp:3729
QgsShapeburstFillSymbolLayer::renderPolygon
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Renders the fill symbol layer for the polygon whose outer ring is defined by points,...
Definition: qgsfillsymbollayer.cpp:1182
QgsPointPatternFillSymbolLayer::mDisplacementX
double mDisplacementX
Definition: qgsfillsymbollayer.h:1776
QgsSimpleFillSymbolLayer::mOffset
QPointF mOffset
Definition: qgsfillsymbollayer.h:189
QgsGeometry::isNull
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsLinePatternFillSymbolLayer::distance
double distance() const
Returns the distance between lines in the fill pattern.
Definition: qgsfillsymbollayer.h:1350
QgsGradientFillSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:972
QgsSVGFillSymbolLayer::layerType
QString layerType() const override
Returns a string that represents this layer type.
Definition: qgsfillsymbollayer.cpp:1995
QgsGradientFillSymbolLayer::GradientCoordinateMode
GradientCoordinateMode
Definition: qgsfillsymbollayer.h:222
QgsLineSymbol::width
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
Definition: qgssymbol.cpp:2000
QgsSymbolLayer::PropertyFillStyle
@ PropertyFillStyle
Fill style (eg solid, dots)
Definition: qgssymbollayer.h:144
QgsSymbolLayer::PropertyGradientReference2Y
@ PropertyGradientReference2Y
Gradient reference point 2 y.
Definition: qgssymbollayer.h:155
QgsRenderContext::setFlag
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Definition: qgsrendercontext.cpp:179
QgsImageFillSymbolLayer::applyDataDefinedSettings
virtual void applyDataDefinedSettings(QgsSymbolRenderContext &context)
Definition: qgsfillsymbollayer.h:778
QgsSimpleFillSymbolLayer::offset
QPointF offset()
Returns the offset by which polygons will be translated during rendering.
Definition: qgsfillsymbollayer.h:114
QgsGradientFillSymbolLayer::mSelBrush
QBrush mSelBrush
Definition: qgsfillsymbollayer.h:378
QgsSimpleFillSymbolLayer::mOffsetMapUnitScale
QgsMapUnitScale mOffsetMapUnitScale
Definition: qgsfillsymbollayer.h:191
QgsRasterFillSymbolLayer::applyBrushTransformFromContext
bool applyBrushTransformFromContext() const override
Returns true if the image brush should be transformed using the render context's texture origin.
Definition: qgsfillsymbollayer.cpp:4237
QgsLineSymbol::clone
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2193
QgsSymbolLayer::color
virtual QColor color() const
The fill color.
Definition: qgssymbollayer.h:227
QgsSymbolLayer::PropertyFillColor
@ PropertyFillColor
Fill color.
Definition: qgssymbollayer.h:135
QgsImageFillSymbolLayer::mStrokeWidthUnit
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Definition: qgsfillsymbollayer.h:772
QgsSymbolLayerUtils::lineToSld
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)
Definition: qgssymbollayerutils.cpp:2001
QgsPointPatternFillSymbolLayer::usedAttributes
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Definition: qgsfillsymbollayer.cpp:3667
QgsLinePatternFillSymbolLayer::lineAngle
double lineAngle() const
Returns the angle for the parallel lines used to fill the symbol.
Definition: qgsfillsymbollayer.h:1335
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:491
QgsGradientFillSymbolLayer::mBrush
QBrush mBrush
Definition: qgsfillsymbollayer.h:377
QgsCentroidFillSymbolLayer::mBiggestPartIndex
int mBiggestPartIndex
Definition: qgsfillsymbollayer.h:2108
QgsExpressionContext::EXPR_GEOMETRY_POINT_NUM
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
Definition: qgsexpressioncontext.h:733
QgsExpressionContext::setOriginalValueVariable
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
Definition: qgsexpressioncontext.cpp:566
qgssymbollayerutils.h
QgsImageFillSymbolLayer::mapUnitScale
QgsMapUnitScale mapUnitScale() const override
Definition: qgsfillsymbollayer.cpp:1735
QgsCentroidFillSymbolLayer::subSymbol
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
Definition: qgsfillsymbollayer.cpp:3921
QgsImageFillSymbolLayer::outputUnit
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:1725
QgsPointPatternFillSymbolLayer::mOffsetY
double mOffsetY
Definition: qgsfillsymbollayer.h:1785
QgsSymbolRenderContext::opacity
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:764
QgsImageOperation::multiplyOpacity
static void multiplyOpacity(QImage &image, double factor)
Multiplies opacity of image pixel values by a factor.
Definition: qgsimageoperation.cpp:322
QgsGradientFillSymbolLayer::GradientSpread
GradientSpread
Definition: qgsfillsymbollayer.h:228
QgsSimpleFillSymbolLayer::mStrokeWidthMapUnitScale
QgsMapUnitScale mStrokeWidthMapUnitScale
Definition: qgsfillsymbollayer.h:184
QgsPointPatternFillSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:3099
QgsSymbolLayerUtils::createMarkerLayerFromSld
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
Definition: qgssymbollayerutils.cpp:1430
QgsSymbolLayer::hasDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Definition: qgssymbollayer.cpp:216
QgsRandomMarkerFillSymbolLayer::setMapUnitScale
void setMapUnitScale(const QgsMapUnitScale &scale) override
Definition: qgsfillsymbollayer.cpp:4624
QgsSymbolLayer::PropertyGradientReference1IsCentroid
@ PropertyGradientReference1IsCentroid
Gradient reference point 1 is centroid.
Definition: qgssymbollayer.h:156
QgsRasterFillSymbolLayer::setOpacity
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
Definition: qgsfillsymbollayer.cpp:4182
QgsRandomMarkerFillSymbolLayer::create
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsRandomMarkerFillSymbolLayer using the specified properties map containing symbol pro...
Definition: qgsfillsymbollayer.cpp:4290
qgsmarkersymbollayer.h
QgsCentroidFillSymbolLayer::toSld
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Definition: qgsfillsymbollayer.cpp:3896
QgsSvgCache::svgAsImage
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false)
Gets SVG as QImage.
Definition: qgssvgcache.cpp:122
QgsRandomMarkerFillSymbolLayer::hasDataDefinedProperties
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Definition: qgsfillsymbollayer.cpp:4535
QgsSimpleFillSymbolLayer::properties
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Definition: qgsfillsymbollayer.cpp:331
QgsSVGFillSymbolLayer::svgFillColor
QColor svgFillColor() const
Returns the fill color used for rendering the SVG content.
Definition: qgsfillsymbollayer.h:1127
QgsRandomMarkerFillSymbolLayer::outputUnit
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:4615
QgsUnitTypes::RenderPercentage
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:171
QgsSymbolLayer::PropertyDisplacementX
@ PropertyDisplacementX
Horizontal displacement.
Definition: qgssymbollayer.h:165
QgsFillSymbolLayer::_renderPolygon
void _renderPolygon(QPainter *p, const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context)
Default method to render polygon.
Definition: qgssymbollayer.cpp:746
QgsGradientFillSymbolLayer::Conical
@ Conical
Definition: qgsfillsymbollayer.h:218
QgsSVGFillSymbolLayer::mapUnitScale
QgsMapUnitScale mapUnitScale() const override
Definition: qgsfillsymbollayer.cpp:1861
DEFAULT_SIMPLEFILL_COLOR
#define DEFAULT_SIMPLEFILL_COLOR
Definition: qgsfillsymbollayer.h:23
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsPointPatternFillSymbolLayer::clone
QgsPointPatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Definition: qgsfillsymbollayer.cpp:3540
QgsRasterFillSymbolLayer
A class for filling symbols with a repeated raster image.
Definition: qgsfillsymbollayer.h:800
QgsCentroidFillSymbolLayer::create
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsCentroidFillSymbolLayer using the specified properties map containing symbol propert...
Definition: qgsfillsymbollayer.cpp:3706
QgsGradientFillSymbolLayer::mColor2
QColor mColor2
Definition: qgsfillsymbollayer.h:381
QgsSymbol::hasDataDefinedProperties
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:771
QgsSymbolLayerUtils::multiplyImageOpacity
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
Definition: qgssymbollayerutils.cpp:3737
QgsSymbolLayer::SELECTION_IS_OPAQUE
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
Definition: qgssymbollayer.h:537
qgsunittypes.h
QgsLinePatternFillSymbolLayer::offset
double offset() const
Returns the offset distance for lines within the fill, which is the distance to offset the parallel l...
Definition: qgsfillsymbollayer.h:1395
QgsSimpleFillSymbolLayer::dxfBrushStyle
Qt::BrushStyle dxfBrushStyle() const override
Gets brush/fill style.
Definition: qgsfillsymbollayer.cpp:489
QgsSymbolLayerUtils::encodeBrushStyle
static QString encodeBrushStyle(Qt::BrushStyle style)
Definition: qgssymbollayerutils.cpp:269
QgsPointPatternFillSymbolLayer::offsetY
double offsetY() const
Returns the vertical offset values for points in the pattern.
Definition: qgsfillsymbollayer.h:1612
QgsLinePatternFillSymbolLayer::clone
QgsLinePatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Definition: qgsfillsymbollayer.cpp:2912
QgsMarkerSymbolLayer
Abstract base class for marker symbol layers.
Definition: qgssymbollayer.h:582
QgsGradientFillSymbolLayer::mGradientSpread
GradientSpread mGradientSpread
Definition: qgsfillsymbollayer.h:385
QgsSymbolLayer::estimateMaxBleed
virtual double estimateMaxBleed(const QgsRenderContext &context) const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Definition: qgssymbollayer.h:373
QgsImageFillSymbolLayer::mNextAngle
double mNextAngle
Definition: qgsfillsymbollayer.h:768
QgsRasterFillSymbolLayer::width
double width() const
Returns the width used for scaling the image used in the fill.
Definition: qgsfillsymbollayer.h:958
QgsImageFillSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:1720
QgsSymbolLayerUtils::encodePenStyle
static QString encodePenStyle(Qt::PenStyle style)
Definition: qgssymbollayerutils.cpp:141
QgsSymbol
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:64
qgsimageoperation.h
QgsSymbolLayerUtils::svgSymbolPathToName
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
Definition: qgssymbollayerutils.cpp:4007
QgsMarkerLineSymbolLayer
Line symbol layer type which draws repeating marker symbols along a line feature.
Definition: qgslinesymbollayer.h:694
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:333
QgsSimpleFillSymbolLayer::estimateMaxBleed
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Definition: qgsfillsymbollayer.cpp:434
DEFAULT_SIMPLEFILL_STYLE
#define DEFAULT_SIMPLEFILL_STYLE
Definition: qgsfillsymbollayer.h:24
QgsSimpleFillSymbolLayer::mapUnitScale
QgsMapUnitScale mapUnitScale() const override
Definition: qgsfillsymbollayer.cpp:82
QgsLinePatternFillSymbolLayer::usedAttributes
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Definition: qgsfillsymbollayer.cpp:2418
QgsSymbolLayer::PropertyStrokeColor
@ PropertyStrokeColor
Stroke color.
Definition: qgssymbollayer.h:136
QgsSymbol::symbolLayer
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:385
QgsGradientFillSymbolLayer::mGradientType
GradientType mGradientType
Definition: qgsfillsymbollayer.h:383
QgsSVGFillSymbolLayer::patternWidth
double patternWidth() const
Returns the width of the rendered SVG content within the fill (i.e.
Definition: qgsfillsymbollayer.h:1106
QgsColorRamp::properties
virtual QgsStringMap properties() const =0
Returns a string map containing all the color ramp's properties.
QgsGradientFillSymbolLayer::renderPolygon
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Renders the fill symbol layer for the polygon whose outer ring is defined by points,...
Definition: qgsfillsymbollayer.cpp:881
QgsLinePatternFillSymbolLayer::hasDataDefinedProperties
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Definition: qgsfillsymbollayer.cpp:2426
QgsSymbolLayerUtils::decodeColor
static QColor decodeColor(const QString &str)
Definition: qgssymbollayerutils.cpp:57
QgsGradientFillSymbolLayer::Feature
@ Feature
Definition: qgsfillsymbollayer.h:223
QgsGeometry::buffer
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
Definition: qgsgeometry.cpp:1904
QgsDxfExport::mapUnitScaleFactor
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
Definition: qgsdxfexport.cpp:1819
QgsLineSymbol::setWidth
void setWidth(double width)
Sets the width for the whole line symbol.
Definition: qgssymbol.cpp:1960
QgsSimpleFillSymbolLayer::mSelPen
QPen mSelPen
Definition: qgsfillsymbollayer.h:187
QgsRandomMarkerFillSymbolLayer::AbsoluteCount
@ AbsoluteCount
The point count is used as an absolute count of markers.
Definition: qgsfillsymbollayer.h:1817
QgsPointPatternFillSymbolLayer::outputUnit
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:3115
QgsSymbolLayerUtils::wellKnownMarkerToSld
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)
Definition: qgssymbollayerutils.cpp:2398
QgsImageFillSymbolLayer::dxfWidth
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
Definition: qgsfillsymbollayer.cpp:1750
QgsGradientFillSymbolLayer::mapUnitScale
QgsMapUnitScale mapUnitScale() const override