QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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"
36 
37 #include <QPainter>
38 #include <QFile>
39 #include <QSvgRenderer>
40 #include <QDomDocument>
41 #include <QDomElement>
42 #include <random>
43 
44 QgsSimpleFillSymbolLayer::QgsSimpleFillSymbolLayer( const QColor &color, Qt::BrushStyle style, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth,
45  Qt::PenJoinStyle penJoinStyle )
46  : mBrushStyle( style )
47  , mStrokeColor( strokeColor )
48  , mStrokeStyle( strokeStyle )
49  , mStrokeWidth( strokeWidth )
50  , mPenJoinStyle( penJoinStyle )
51 {
52  mColor = color;
53 }
54 
56 {
57  mStrokeWidthUnit = unit;
58  mOffsetUnit = unit;
59 }
60 
62 {
64  if ( mOffsetUnit != unit )
65  {
67  }
68  return unit;
69 }
70 
72 {
74  mOffsetMapUnitScale = scale;
75 }
76 
78 {
80  {
82  }
83  return QgsMapUnitScale();
84 }
85 
86 void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
87 {
88  if ( !dataDefinedProperties().hasActiveProperties() )
89  return; // shortcut
90 
91  bool ok;
92 
94  {
97  }
99  {
102  if ( exprVal.isValid() )
103  brush.setStyle( QgsSymbolLayerUtils::decodeBrushStyle( exprVal.toString() ) );
104  }
106  {
109  }
111  {
114  double width = exprVal.toDouble( &ok );
115  if ( ok )
116  {
118  pen.setWidthF( width );
119  selPen.setWidthF( width );
120  }
121  }
123  {
126  if ( ok )
127  {
128  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
129  selPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( style ) );
130  }
131  }
133  {
136  if ( ok )
137  {
138  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
139  selPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
140  }
141  }
142 }
143 
144 
146 {
148  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
152  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
153  QPointF offset;
154 
155  if ( props.contains( QStringLiteral( "color" ) ) )
156  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
157  if ( props.contains( QStringLiteral( "style" ) ) )
158  style = QgsSymbolLayerUtils::decodeBrushStyle( props[QStringLiteral( "style" )] );
159  if ( props.contains( QStringLiteral( "color_border" ) ) )
160  {
161  //pre 2.5 projects used "color_border"
162  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
163  }
164  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
165  {
166  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
167  }
168  else if ( props.contains( QStringLiteral( "line_color" ) ) )
169  {
170  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
171  }
172 
173  if ( props.contains( QStringLiteral( "style_border" ) ) )
174  {
175  //pre 2.5 projects used "style_border"
176  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "style_border" )] );
177  }
178  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
179  {
180  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] );
181  }
182  else if ( props.contains( QStringLiteral( "line_style" ) ) )
183  {
184  strokeStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] );
185  }
186  if ( props.contains( QStringLiteral( "width_border" ) ) )
187  {
188  //pre 2.5 projects used "width_border"
189  strokeWidth = props[QStringLiteral( "width_border" )].toDouble();
190  }
191  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
192  {
193  strokeWidth = props[QStringLiteral( "outline_width" )].toDouble();
194  }
195  else if ( props.contains( QStringLiteral( "line_width" ) ) )
196  {
197  strokeWidth = props[QStringLiteral( "line_width" )].toDouble();
198  }
199  if ( props.contains( QStringLiteral( "offset" ) ) )
200  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
201  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
202  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
203 
204  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( color, style, strokeColor, strokeStyle, strokeWidth, penJoinStyle );
205  sl->setOffset( offset );
206  if ( props.contains( QStringLiteral( "border_width_unit" ) ) )
207  {
208  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "border_width_unit" )] ) );
209  }
210  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
211  {
212  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
213  }
214  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
215  {
216  sl->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
217  }
218  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
219  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
220 
221  if ( props.contains( QStringLiteral( "border_width_map_unit_scale" ) ) )
222  sl->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "border_width_map_unit_scale" )] ) );
223  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
224  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
225 
226  sl->restoreOldDataDefinedProperties( props );
227 
228  return sl.release();
229 }
230 
231 
233 {
234  return QStringLiteral( "SimpleFill" );
235 }
236 
238 {
239  QColor fillColor = mColor;
240  fillColor.setAlphaF( context.opacity() * mColor.alphaF() );
241  mBrush = QBrush( fillColor, mBrushStyle );
242 
243  QColor selColor = context.renderContext().selectionColor();
244  QColor selPenColor = selColor == mColor ? selColor : mStrokeColor;
245  if ( ! SELECTION_IS_OPAQUE ) selColor.setAlphaF( context.opacity() );
246  mSelBrush = QBrush( selColor );
247  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
248  // this would mean symbols with "no fill" look the same whether or not they are selected
249  if ( SELECT_FILL_STYLE )
250  mSelBrush.setStyle( mBrushStyle );
251 
252  QColor strokeColor = mStrokeColor;
253  strokeColor.setAlphaF( context.opacity() * mStrokeColor.alphaF() );
254  mPen = QPen( strokeColor );
255  mSelPen = QPen( selPenColor );
256  mPen.setStyle( mStrokeStyle );
258  mPen.setJoinStyle( mPenJoinStyle );
259 }
260 
262 {
263  Q_UNUSED( context )
264 }
265 
266 void QgsSimpleFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
267 {
268  QPainter *p = context.renderContext().painter();
269  if ( !p )
270  {
271  return;
272  }
273 
274  applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );
275 
276  p->setBrush( context.selected() ? mSelBrush : mBrush );
277  p->setPen( context.selected() ? mSelPen : mPen );
278 
279  QPointF offset;
280  if ( !mOffset.isNull() )
281  {
284  p->translate( offset );
285  }
286 
287  _renderPolygon( p, points, rings, context );
288 
289  if ( !mOffset.isNull() )
290  {
291  p->translate( -offset );
292  }
293 }
294 
296 {
297  QgsStringMap map;
298  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
299  map[QStringLiteral( "style" )] = QgsSymbolLayerUtils::encodeBrushStyle( mBrushStyle );
300  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
301  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
302  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
303  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
304  map[QStringLiteral( "border_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
305  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
306  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
307  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
308  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
309  return map;
310 }
311 
313 {
314  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( mColor, mBrushStyle, mStrokeColor, mStrokeStyle, mStrokeWidth, mPenJoinStyle );
315  sl->setOffset( mOffset );
316  sl->setOffsetUnit( mOffsetUnit );
317  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
318  sl->setStrokeWidthUnit( mStrokeWidthUnit );
319  sl->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
320  copyDataDefinedProperties( sl.get() );
321  copyPaintEffect( sl.get() );
322  return sl.release();
323 }
324 
325 void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
326 {
327  if ( mBrushStyle == Qt::NoBrush && mStrokeStyle == Qt::NoPen )
328  return;
329 
330  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
331  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
332  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
333  element.appendChild( symbolizerElem );
334 
335  // <Geometry>
336  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
337 
338  if ( mBrushStyle != Qt::NoBrush )
339  {
340  // <Fill>
341  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
342  symbolizerElem.appendChild( fillElem );
344  }
345 
346  if ( mStrokeStyle != Qt::NoPen )
347  {
348  // <Stroke>
349  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
350  symbolizerElem.appendChild( strokeElem );
352  QgsSymbolLayerUtils::lineToSld( doc, strokeElem, mStrokeStyle, mStrokeColor, strokeWidth, &mPenJoinStyle );
353  }
354 
355  // <se:Displacement>
357  QgsSymbolLayerUtils::createDisplacementElement( doc, symbolizerElem, offset );
358 }
359 
360 QString QgsSimpleFillSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
361 {
362  //brush
363  QString symbolStyle;
364  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStyleBrush( mColor ) );
365  symbolStyle.append( ';' );
366  //pen
367  symbolStyle.append( QgsSymbolLayerUtils::ogrFeatureStylePen( mStrokeWidth, mmScaleFactor, mapUnitScaleFactor, mStrokeColor, mPenJoinStyle ) );
368  return symbolStyle;
369 }
370 
372 {
373  QColor color, strokeColor;
374  Qt::BrushStyle fillStyle;
375  Qt::PenStyle strokeStyle;
376  double strokeWidth;
377 
378  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
379  QgsSymbolLayerUtils::fillFromSld( fillElem, fillStyle, color );
380 
381  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
382  QgsSymbolLayerUtils::lineFromSld( strokeElem, strokeStyle, strokeColor, strokeWidth );
383 
384  QPointF offset;
386 
387  QString uom = element.attribute( QStringLiteral( "uom" ), QString() );
388  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
389  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
390  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
391 
392  std::unique_ptr< QgsSimpleFillSymbolLayer > sl = qgis::make_unique< QgsSimpleFillSymbolLayer >( color, fillStyle, strokeColor, strokeStyle, strokeWidth );
393  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
394  sl->setOffset( offset );
395  return sl.release();
396 }
397 
399 {
400  double penBleed = context.convertToPainterUnits( mStrokeStyle == Qt::NoPen ? 0 : ( mStrokeWidth / 2.0 ), mStrokeWidthUnit, mStrokeWidthMapUnitScale );
401  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
402  return penBleed + offsetBleed;
403 }
404 
406 {
407  double width = mStrokeWidth;
409  {
412  }
414 }
415 
417 {
418  QColor c = mStrokeColor;
420  {
423  }
424  return c;
425 }
426 
428 {
429  double angle = mAngle;
431  {
432  context.setOriginalValueVariable( mAngle );
434  }
435  return angle;
436 }
437 
439 {
440  return mStrokeStyle;
441 }
442 
444 {
445  QColor c = mColor;
447  {
449  }
450  return c;
451 }
452 
454 {
455  return mBrushStyle;
456 }
457 
458 //QgsGradientFillSymbolLayer
459 
461  GradientColorType colorType, GradientType gradientType,
462  GradientCoordinateMode coordinateMode, GradientSpread spread )
463  : mGradientColorType( colorType )
464  , mGradientType( gradientType )
465  , mCoordinateMode( coordinateMode )
466  , mGradientSpread( spread )
467  , mReferencePoint1( QPointF( 0.5, 0 ) )
468  , mReferencePoint2( QPointF( 0.5, 1 ) )
469 {
470  mColor = color;
471  mColor2 = color2;
472 }
473 
475 {
476  delete mGradientRamp;
477 }
478 
480 {
481  //default to a two-color, linear gradient with feature mode and pad spreading
486  //default to gradient from the default fill color to white
487  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
488  QPointF referencePoint1 = QPointF( 0.5, 0 );
489  bool refPoint1IsCentroid = false;
490  QPointF referencePoint2 = QPointF( 0.5, 1 );
491  bool refPoint2IsCentroid = false;
492  double angle = 0;
493  QPointF offset;
494 
495  //update gradient properties from props
496  if ( props.contains( QStringLiteral( "type" ) ) )
497  type = static_cast< GradientType >( props[QStringLiteral( "type" )].toInt() );
498  if ( props.contains( QStringLiteral( "coordinate_mode" ) ) )
499  coordinateMode = static_cast< GradientCoordinateMode >( props[QStringLiteral( "coordinate_mode" )].toInt() );
500  if ( props.contains( QStringLiteral( "spread" ) ) )
501  gradientSpread = static_cast< GradientSpread >( props[QStringLiteral( "spread" )].toInt() );
502  if ( props.contains( QStringLiteral( "color_type" ) ) )
503  colorType = static_cast< GradientColorType >( props[QStringLiteral( "color_type" )].toInt() );
504  if ( props.contains( QStringLiteral( "gradient_color" ) ) )
505  {
506  //pre 2.5 projects used "gradient_color"
507  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color" )] );
508  }
509  else if ( props.contains( QStringLiteral( "color" ) ) )
510  {
511  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
512  }
513  if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
514  {
515  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
516  }
517 
518  if ( props.contains( QStringLiteral( "reference_point1" ) ) )
519  referencePoint1 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point1" )] );
520  if ( props.contains( QStringLiteral( "reference_point1_iscentroid" ) ) )
521  refPoint1IsCentroid = props[QStringLiteral( "reference_point1_iscentroid" )].toInt();
522  if ( props.contains( QStringLiteral( "reference_point2" ) ) )
523  referencePoint2 = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "reference_point2" )] );
524  if ( props.contains( QStringLiteral( "reference_point2_iscentroid" ) ) )
525  refPoint2IsCentroid = props[QStringLiteral( "reference_point2_iscentroid" )].toInt();
526  if ( props.contains( QStringLiteral( "angle" ) ) )
527  angle = props[QStringLiteral( "angle" )].toDouble();
528 
529  if ( props.contains( QStringLiteral( "offset" ) ) )
530  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
531 
532  //attempt to create color ramp from props
533  QgsColorRamp *gradientRamp = nullptr;
534  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
535  {
536  gradientRamp = QgsCptCityColorRamp::create( props );
537  }
538  else
539  {
540  gradientRamp = QgsGradientColorRamp::create( props );
541  }
542 
543  //create a new gradient fill layer with desired properties
544  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( color, color2, colorType, type, coordinateMode, gradientSpread );
545  sl->setOffset( offset );
546  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
547  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
548  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
549  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
550  sl->setReferencePoint1( referencePoint1 );
551  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
552  sl->setReferencePoint2( referencePoint2 );
553  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
554  sl->setAngle( angle );
555  if ( gradientRamp )
556  sl->setColorRamp( gradientRamp );
557 
558  sl->restoreOldDataDefinedProperties( props );
559 
560  return sl.release();
561 }
562 
564 {
565  delete mGradientRamp;
566  mGradientRamp = ramp;
567 }
568 
570 {
571  return QStringLiteral( "GradientFill" );
572 }
573 
574 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, const QPolygonF &points )
575 {
576  if ( !dataDefinedProperties().hasActiveProperties() && !mReferencePoint1IsCentroid && !mReferencePoint2IsCentroid )
577  {
578  //shortcut
581  return;
582  }
583 
584  bool ok;
585 
586  //first gradient color
587  QColor color = mColor;
589  {
592  }
593 
594  //second gradient color
595  QColor color2 = mColor2;
597  {
600  }
601 
602  //gradient rotation angle
603  double angle = mAngle;
605  {
606  context.setOriginalValueVariable( mAngle );
608  }
609 
610  //gradient type
613  {
615  if ( ok )
616  {
617  if ( currentType == QObject::tr( "linear" ) )
618  {
619  gradientType = QgsGradientFillSymbolLayer::Linear;
620  }
621  else if ( currentType == QObject::tr( "radial" ) )
622  {
623  gradientType = QgsGradientFillSymbolLayer::Radial;
624  }
625  else if ( currentType == QObject::tr( "conical" ) )
626  {
628  }
629  }
630  }
631 
632  //coordinate mode
635  {
636  QString currentCoordMode = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCoordinateMode, context.renderContext().expressionContext(), QString(), &ok );
637  if ( ok )
638  {
639  if ( currentCoordMode == QObject::tr( "feature" ) )
640  {
641  coordinateMode = QgsGradientFillSymbolLayer::Feature;
642  }
643  else if ( currentCoordMode == QObject::tr( "viewport" ) )
644  {
645  coordinateMode = QgsGradientFillSymbolLayer::Viewport;
646  }
647  }
648  }
649 
650  //gradient spread
653  {
655  if ( ok )
656  {
657  if ( currentSpread == QObject::tr( "pad" ) )
658  {
660  }
661  else if ( currentSpread == QObject::tr( "repeat" ) )
662  {
664  }
665  else if ( currentSpread == QObject::tr( "reflect" ) )
666  {
668  }
669  }
670  }
671 
672  //reference point 1 x & y
673  double refPoint1X = mReferencePoint1.x();
675  {
676  context.setOriginalValueVariable( refPoint1X );
678  }
679  double refPoint1Y = mReferencePoint1.y();
681  {
682  context.setOriginalValueVariable( refPoint1Y );
684  }
685  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
687  {
688  context.setOriginalValueVariable( refPoint1IsCentroid );
690  }
691 
692  //reference point 2 x & y
693  double refPoint2X = mReferencePoint2.x();
695  {
696  context.setOriginalValueVariable( refPoint2X );
698  }
699  double refPoint2Y = mReferencePoint2.y();
701  {
702  context.setOriginalValueVariable( refPoint2Y );
704  }
705  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
707  {
708  context.setOriginalValueVariable( refPoint2IsCentroid );
710  }
711 
712  if ( refPoint1IsCentroid || refPoint2IsCentroid )
713  {
714  //either the gradient is starting or ending at a centroid, so calculate it
715  QPointF centroid = QgsSymbolLayerUtils::polygonCentroid( points );
716  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
717  QRectF bbox = points.boundingRect();
718  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
719  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
720 
721  if ( refPoint1IsCentroid )
722  {
723  refPoint1X = centroidX;
724  refPoint1Y = centroidY;
725  }
726  if ( refPoint2IsCentroid )
727  {
728  refPoint2X = centroidX;
729  refPoint2Y = centroidY;
730  }
731  }
732 
733  //update gradient with data defined values
734  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
735  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
736 }
737 
738 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint, double angle )
739 {
740  //rotate a reference point by a specified angle around the point (0.5, 0.5)
741 
742  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
743  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
744  //rotate this line by the current rotation angle
745  refLine.setAngle( refLine.angle() + angle );
746  //get new end point of line
747  QPointF rotatedReferencePoint = refLine.p2();
748  //make sure coords of new end point is within [0, 1]
749  if ( rotatedReferencePoint.x() > 1 )
750  rotatedReferencePoint.setX( 1 );
751  if ( rotatedReferencePoint.x() < 0 )
752  rotatedReferencePoint.setX( 0 );
753  if ( rotatedReferencePoint.y() > 1 )
754  rotatedReferencePoint.setY( 1 );
755  if ( rotatedReferencePoint.y() < 0 )
756  rotatedReferencePoint.setY( 0 );
757 
758  return rotatedReferencePoint;
759 }
760 
761 void QgsGradientFillSymbolLayer::applyGradient( const QgsSymbolRenderContext &context, QBrush &brush,
762  const QColor &color, const QColor &color2, GradientColorType gradientColorType,
763  QgsColorRamp *gradientRamp, GradientType gradientType,
765  QPointF referencePoint1, QPointF referencePoint2, const double angle )
766 {
767  //update alpha of gradient colors
768  QColor fillColor = color;
769  fillColor.setAlphaF( context.opacity() * fillColor.alphaF() );
770  QColor fillColor2 = color2;
771  fillColor2.setAlphaF( context.opacity() * fillColor2.alphaF() );
772 
773  //rotate reference points
774  QPointF rotatedReferencePoint1 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
775  QPointF rotatedReferencePoint2 = !qgsDoubleNear( angle, 0.0 ) ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
776 
777  //create a QGradient with the desired properties
778  QGradient gradient;
779  switch ( gradientType )
780  {
782  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
783  break;
785  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
786  break;
788  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
789  break;
790  }
791  switch ( coordinateMode )
792  {
794  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
795  break;
797  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
798  break;
799  }
800  switch ( gradientSpread )
801  {
803  gradient.setSpread( QGradient::PadSpread );
804  break;
806  gradient.setSpread( QGradient::ReflectSpread );
807  break;
809  gradient.setSpread( QGradient::RepeatSpread );
810  break;
811  }
812 
813  //add stops to gradient
815  ( gradientRamp->type() == QLatin1String( "gradient" ) || gradientRamp->type() == QLatin1String( "cpt-city" ) ) )
816  {
817  //color ramp gradient
818  QgsGradientColorRamp *gradRamp = static_cast<QgsGradientColorRamp *>( gradientRamp );
819  gradRamp->addStopsToGradient( &gradient, context.opacity() );
820  }
821  else
822  {
823  //two color gradient
824  gradient.setColorAt( 0.0, fillColor );
825  gradient.setColorAt( 1.0, fillColor2 );
826  }
827 
828  //update QBrush use gradient
829  brush = QBrush( gradient );
830 }
831 
833 {
834  QColor selColor = context.renderContext().selectionColor();
835  if ( ! SELECTION_IS_OPAQUE )
836  selColor.setAlphaF( context.opacity() );
837  mSelBrush = QBrush( selColor );
838 }
839 
841 {
842  Q_UNUSED( context )
843 }
844 
845 void QgsGradientFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
846 {
847  QPainter *p = context.renderContext().painter();
848  if ( !p )
849  {
850  return;
851  }
852 
853  applyDataDefinedSymbology( context, points );
854 
855  p->setBrush( context.selected() ? mSelBrush : mBrush );
856  p->setPen( Qt::NoPen );
857 
858  QPointF offset;
859  if ( !mOffset.isNull() )
860  {
863  p->translate( offset );
864  }
865 
866  _renderPolygon( p, points, rings, context );
867 
868  if ( !mOffset.isNull() )
869  {
870  p->translate( -offset );
871  }
872 }
873 
875 {
876  QgsStringMap map;
877  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
878  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
879  map[QStringLiteral( "color_type" )] = QString::number( mGradientColorType );
880  map[QStringLiteral( "type" )] = QString::number( mGradientType );
881  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
882  map[QStringLiteral( "spread" )] = QString::number( mGradientSpread );
883  map[QStringLiteral( "reference_point1" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint1 );
884  map[QStringLiteral( "reference_point1_iscentroid" )] = QString::number( mReferencePoint1IsCentroid );
885  map[QStringLiteral( "reference_point2" )] = QgsSymbolLayerUtils::encodePoint( mReferencePoint2 );
886  map[QStringLiteral( "reference_point2_iscentroid" )] = QString::number( mReferencePoint2IsCentroid );
887  map[QStringLiteral( "angle" )] = QString::number( mAngle );
888  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
889  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
890  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
891  if ( mGradientRamp )
892  {
893  map.unite( mGradientRamp->properties() );
894  }
895  return map;
896 }
897 
899 {
900  std::unique_ptr< QgsGradientFillSymbolLayer > sl = qgis::make_unique< QgsGradientFillSymbolLayer >( mColor, mColor2, mGradientColorType, mGradientType, mCoordinateMode, mGradientSpread );
901  if ( mGradientRamp )
902  sl->setColorRamp( mGradientRamp->clone() );
903  sl->setReferencePoint1( mReferencePoint1 );
904  sl->setReferencePoint1IsCentroid( mReferencePoint1IsCentroid );
905  sl->setReferencePoint2( mReferencePoint2 );
906  sl->setReferencePoint2IsCentroid( mReferencePoint2IsCentroid );
907  sl->setAngle( mAngle );
908  sl->setOffset( mOffset );
909  sl->setOffsetUnit( mOffsetUnit );
910  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
911  copyDataDefinedProperties( sl.get() );
912  copyPaintEffect( sl.get() );
913  return sl.release();
914 }
915 
917 {
918  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
919  return offsetBleed;
920 }
921 
923 {
924  mOffsetUnit = unit;
925 }
926 
928 {
929  return mOffsetUnit;
930 }
931 
933 {
934  mOffsetMapUnitScale = scale;
935 }
936 
938 {
939  return mOffsetMapUnitScale;
940 }
941 
942 //QgsShapeburstFillSymbolLayer
943 
945  int blurRadius, bool useWholeShape, double maxDistance )
946  : mBlurRadius( blurRadius )
947  , mUseWholeShape( useWholeShape )
948  , mMaxDistance( maxDistance )
949  , mColorType( colorType )
950  , mColor2( color2 )
951 {
952  mColor = color;
953 }
954 
956 
958 {
959  //default to a two-color gradient
961  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
962  int blurRadius = 0;
963  bool useWholeShape = true;
964  double maxDistance = 5;
965  QPointF offset;
966 
967  //update fill properties from props
968  if ( props.contains( QStringLiteral( "color_type" ) ) )
969  {
970  colorType = static_cast< ShapeburstColorType >( props[QStringLiteral( "color_type" )].toInt() );
971  }
972  if ( props.contains( QStringLiteral( "shapeburst_color" ) ) )
973  {
974  //pre 2.5 projects used "shapeburst_color"
975  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color" )] );
976  }
977  else if ( props.contains( QStringLiteral( "color" ) ) )
978  {
979  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
980  }
981 
982  if ( props.contains( QStringLiteral( "shapeburst_color2" ) ) )
983  {
984  //pre 2.5 projects used "shapeburst_color2"
985  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "shapeburst_color2" )] );
986  }
987  else if ( props.contains( QStringLiteral( "gradient_color2" ) ) )
988  {
989  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "gradient_color2" )] );
990  }
991  if ( props.contains( QStringLiteral( "blur_radius" ) ) )
992  {
993  blurRadius = props[QStringLiteral( "blur_radius" )].toInt();
994  }
995  if ( props.contains( QStringLiteral( "use_whole_shape" ) ) )
996  {
997  useWholeShape = props[QStringLiteral( "use_whole_shape" )].toInt();
998  }
999  if ( props.contains( QStringLiteral( "max_distance" ) ) )
1000  {
1001  maxDistance = props[QStringLiteral( "max_distance" )].toDouble();
1002  }
1003  if ( props.contains( QStringLiteral( "offset" ) ) )
1004  {
1005  offset = QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] );
1006  }
1007 
1008  //attempt to create color ramp from props
1009  QgsColorRamp *gradientRamp = nullptr;
1010  if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QStringLiteral( "cpt-city" ) )
1011  {
1012  gradientRamp = QgsCptCityColorRamp::create( props );
1013  }
1014  else
1015  {
1016  gradientRamp = QgsGradientColorRamp::create( props );
1017  }
1018 
1019  //create a new shapeburst fill layer with desired properties
1020  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1021  sl->setOffset( offset );
1022  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1023  {
1024  sl->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1025  }
1026  if ( props.contains( QStringLiteral( "distance_unit" ) ) )
1027  {
1028  sl->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "distance_unit" )] ) );
1029  }
1030  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1031  {
1032  sl->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1033  }
1034  if ( props.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
1035  {
1036  sl->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "distance_map_unit_scale" )] ) );
1037  }
1038  if ( props.contains( QStringLiteral( "ignore_rings" ) ) )
1039  {
1040  sl->setIgnoreRings( props[QStringLiteral( "ignore_rings" )].toInt() );
1041  }
1042  if ( gradientRamp )
1043  {
1044  sl->setColorRamp( gradientRamp );
1045  }
1046 
1047  sl->restoreOldDataDefinedProperties( props );
1048 
1049  return sl.release();
1050 }
1051 
1053 {
1054  return QStringLiteral( "ShapeburstFill" );
1055 }
1056 
1058 {
1059  if ( mGradientRamp.get() == ramp )
1060  return;
1061 
1062  mGradientRamp.reset( ramp );
1063 }
1064 
1065 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QColor &color, QColor &color2, int &blurRadius, bool &useWholeShape,
1066  double &maxDistance, bool &ignoreRings )
1067 {
1068  //first gradient color
1069  color = mColor;
1071  {
1074  }
1075 
1076  //second gradient color
1077  color2 = mColor2;
1079  {
1082  }
1083 
1084  //blur radius
1085  blurRadius = mBlurRadius;
1087  {
1088  context.setOriginalValueVariable( mBlurRadius );
1090  }
1091 
1092  //use whole shape
1093  useWholeShape = mUseWholeShape;
1095  {
1096  context.setOriginalValueVariable( mUseWholeShape );
1098  }
1099 
1100  //max distance
1101  maxDistance = mMaxDistance;
1103  {
1104  context.setOriginalValueVariable( mMaxDistance );
1106  }
1107 
1108  //ignore rings
1109  ignoreRings = mIgnoreRings;
1111  {
1112  context.setOriginalValueVariable( mIgnoreRings );
1114  }
1115 
1116 }
1117 
1119 {
1120  //TODO - check this
1121  QColor selColor = context.renderContext().selectionColor();
1122  if ( ! SELECTION_IS_OPAQUE )
1123  selColor.setAlphaF( context.opacity() );
1124  mSelBrush = QBrush( selColor );
1125 }
1126 
1128 {
1129  Q_UNUSED( context )
1130 }
1131 
1132 void QgsShapeburstFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1133 {
1134  QPainter *p = context.renderContext().painter();
1135  if ( !p )
1136  {
1137  return;
1138  }
1139 
1140  if ( context.selected() )
1141  {
1142  //feature is selected, draw using selection style
1143  p->setBrush( mSelBrush );
1144  QPointF offset;
1145  if ( !mOffset.isNull() )
1146  {
1147  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1148  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1149  p->translate( offset );
1150  }
1151  _renderPolygon( p, points, rings, context );
1152  if ( !mOffset.isNull() )
1153  {
1154  p->translate( -offset );
1155  }
1156  return;
1157  }
1158 
1159  QColor color1, color2;
1160  int blurRadius;
1161  bool useWholeShape;
1162  double maxDistance;
1163  bool ignoreRings;
1164  //calculate data defined symbology
1165  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1166 
1167  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1168  int outputPixelMaxDist = 0;
1169  if ( !useWholeShape && !qgsDoubleNear( maxDistance, 0.0 ) )
1170  {
1171  //convert max distance to pixels
1172  outputPixelMaxDist = static_cast< int >( std::round( context.renderContext().convertToPainterUnits( maxDistance, mDistanceUnit, mDistanceMapUnitScale ) ) );
1173  }
1174 
1175  //if we are using the two color mode, create a gradient ramp
1176  std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1178  {
1179  twoColorGradientRamp = qgis::make_unique< QgsGradientColorRamp >( color1, color2 );
1180  }
1181 
1182  //no stroke for shapeburst fills
1183  p->setPen( QPen( Qt::NoPen ) );
1184 
1185  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1186  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1187  //create a QImage to draw shapeburst in
1188  int pointsWidth = static_cast< int >( std::round( points.boundingRect().width() ) );
1189  int pointsHeight = static_cast< int >( std::round( points.boundingRect().height() ) );
1190  int imWidth = pointsWidth + ( sideBuffer * 2 );
1191  int imHeight = pointsHeight + ( sideBuffer * 2 );
1192  std::unique_ptr< QImage > fillImage = qgis::make_unique< QImage >( imWidth,
1193  imHeight, QImage::Format_ARGB32_Premultiplied );
1194  if ( fillImage->isNull() )
1195  {
1196  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1197  return;
1198  }
1199 
1200  //also create an image to store the alpha channel
1201  std::unique_ptr< QImage > alphaImage = qgis::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1202  if ( alphaImage->isNull() )
1203  {
1204  QgsMessageLog::logMessage( QObject::tr( "Could not allocate sufficient memory for shapeburst fill" ) );
1205  return;
1206  }
1207 
1208  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1209  //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
1210  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1211  fillImage->fill( Qt::black );
1212 
1213  //initially fill the alpha channel image with a transparent color
1214  alphaImage->fill( Qt::transparent );
1215 
1216  //now, draw the polygon in the alpha channel image
1217  QPainter imgPainter;
1218  imgPainter.begin( alphaImage.get() );
1219  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1220  imgPainter.setBrush( QBrush( Qt::white ) );
1221  imgPainter.setPen( QPen( Qt::black ) );
1222  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1223  _renderPolygon( &imgPainter, points, rings, context );
1224  imgPainter.end();
1225 
1226  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1227  //(this avoids calling _renderPolygon twice, since that can be slow)
1228  imgPainter.begin( fillImage.get() );
1229  if ( !ignoreRings )
1230  {
1231  imgPainter.drawImage( 0, 0, *alphaImage );
1232  }
1233  else
1234  {
1235  //using ignore rings mode, so the alpha image can't be used
1236  //directly as the alpha channel contains polygon rings and we need
1237  //to draw now without any rings
1238  imgPainter.setBrush( QBrush( Qt::white ) );
1239  imgPainter.setPen( QPen( Qt::black ) );
1240  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1241  _renderPolygon( &imgPainter, points, nullptr, context );
1242  }
1243  imgPainter.end();
1244 
1245  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1246  double *dtArray = distanceTransform( fillImage.get(), context.renderContext() );
1247 
1248  //copy distance transform values back to QImage, shading by appropriate color ramp
1249  dtArrayToQImage( dtArray, fillImage.get(), mColorType == QgsShapeburstFillSymbolLayer::SimpleTwoColor ? twoColorGradientRamp.get() : mGradientRamp.get(),
1250  context.renderContext(), useWholeShape, outputPixelMaxDist );
1251  if ( context.opacity() < 1 )
1252  {
1253  QgsImageOperation::multiplyOpacity( *fillImage, context.opacity() );
1254  }
1255 
1256  //clean up some variables
1257  delete [] dtArray;
1258 
1259  //apply blur if desired
1260  if ( blurRadius > 0 )
1261  {
1262  QgsImageOperation::stackBlur( *fillImage, blurRadius, false );
1263  }
1264 
1265  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1266  imgPainter.begin( fillImage.get() );
1267  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1268  imgPainter.drawImage( 0, 0, *alphaImage );
1269  imgPainter.end();
1270  //we're finished with the alpha channel image now
1271  alphaImage.reset();
1272 
1273  //draw shapeburst image in correct place in the destination painter
1274 
1275  p->save();
1276  QPointF offset;
1277  if ( !mOffset.isNull() )
1278  {
1279  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
1280  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
1281  p->translate( offset );
1282  }
1283 
1284  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1285 
1286  if ( !mOffset.isNull() )
1287  {
1288  p->translate( -offset );
1289  }
1290  p->restore();
1291 
1292 }
1293 
1294 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1295 
1296 /* distance transform of a 1d function using squared distance */
1297 void QgsShapeburstFillSymbolLayer::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1298 {
1299  int k = 0;
1300  v[0] = 0;
1301  z[0] = -INF;
1302  z[1] = + INF;
1303  for ( int q = 1; q <= n - 1; q++ )
1304  {
1305  double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1306  while ( s <= z[k] )
1307  {
1308  k--;
1309  s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1310  }
1311  k++;
1312  v[k] = q;
1313  z[k] = s;
1314  z[k + 1] = + INF;
1315  }
1316 
1317  k = 0;
1318  for ( int q = 0; q <= n - 1; q++ )
1319  {
1320  while ( z[k + 1] < q )
1321  k++;
1322  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1323  }
1324 }
1325 
1326 /* distance transform of 2d function using squared distance */
1327 void QgsShapeburstFillSymbolLayer::distanceTransform2d( double *im, int width, int height, QgsRenderContext &context )
1328 {
1329  int maxDimension = std::max( width, height );
1330  double *f = new double[ maxDimension ];
1331  int *v = new int[ maxDimension ];
1332  double *z = new double[ maxDimension + 1 ];
1333  double *d = new double[ maxDimension ];
1334 
1335  // transform along columns
1336  for ( int x = 0; x < width; x++ )
1337  {
1338  if ( context.renderingStopped() )
1339  break;
1340 
1341  for ( int y = 0; y < height; y++ )
1342  {
1343  f[y] = im[ x + y * width ];
1344  }
1345  distanceTransform1d( f, height, v, z, d );
1346  for ( int y = 0; y < height; y++ )
1347  {
1348  im[ x + y * width ] = d[y];
1349  }
1350  }
1351 
1352  // transform along rows
1353  for ( int y = 0; y < height; y++ )
1354  {
1355  if ( context.renderingStopped() )
1356  break;
1357 
1358  for ( int x = 0; x < width; x++ )
1359  {
1360  f[x] = im[ x + y * width ];
1361  }
1362  distanceTransform1d( f, width, v, z, d );
1363  for ( int x = 0; x < width; x++ )
1364  {
1365  im[ x + y * width ] = d[x];
1366  }
1367  }
1368 
1369  delete [] d;
1370  delete [] f;
1371  delete [] v;
1372  delete [] z;
1373 }
1374 
1375 /* distance transform of a binary QImage */
1376 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im, QgsRenderContext &context )
1377 {
1378  int width = im->width();
1379  int height = im->height();
1380 
1381  double *dtArray = new double[width * height];
1382 
1383  //load qImage to array
1384  QRgb tmpRgb;
1385  int idx = 0;
1386  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1387  {
1388  if ( context.renderingStopped() )
1389  break;
1390 
1391  const QRgb *scanLine = reinterpret_cast< const QRgb * >( im->constScanLine( heightIndex ) );
1392  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1393  {
1394  tmpRgb = scanLine[widthIndex];
1395  if ( qRed( tmpRgb ) == 0 )
1396  {
1397  //black pixel, so zero distance
1398  dtArray[ idx ] = 0;
1399  }
1400  else
1401  {
1402  //white pixel, so initially set distance as infinite
1403  dtArray[ idx ] = INF;
1404  }
1405  idx++;
1406  }
1407  }
1408 
1409  //calculate squared distance transform
1410  distanceTransform2d( dtArray, width, height, context );
1411 
1412  return dtArray;
1413 }
1414 
1415 void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, QgsColorRamp *ramp, QgsRenderContext &context, bool useWholeShape, int maxPixelDistance )
1416 {
1417  int width = im->width();
1418  int height = im->height();
1419 
1420  //find maximum distance value
1421  double maxDistanceValue;
1422 
1423  if ( useWholeShape )
1424  {
1425  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1426  double dtMaxValue = array[0];
1427  for ( int i = 1; i < ( width * height ); ++i )
1428  {
1429  if ( array[i] > dtMaxValue )
1430  {
1431  dtMaxValue = array[i];
1432  }
1433  }
1434 
1435  //values in distance transform are squared
1436  maxDistanceValue = std::sqrt( dtMaxValue );
1437  }
1438  else
1439  {
1440  //use max distance set in symbol properties
1441  maxDistanceValue = maxPixelDistance;
1442  }
1443 
1444  //update the pixels in the provided QImage
1445  int idx = 0;
1446  double squaredVal = 0;
1447  double pixVal = 0;
1448 
1449  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1450  {
1451  if ( context.renderingStopped() )
1452  break;
1453 
1454  QRgb *scanLine = reinterpret_cast< QRgb * >( im->scanLine( heightIndex ) );
1455  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1456  {
1457  //result of distance transform
1458  squaredVal = array[idx];
1459 
1460  //scale result to fit in the range [0, 1]
1461  if ( maxDistanceValue > 0 )
1462  {
1463  pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1464  }
1465  else
1466  {
1467  pixVal = 1.0;
1468  }
1469 
1470  //convert value to color from ramp
1471  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1472  scanLine[widthIndex] = qPremultiply( ramp->color( pixVal ).rgba() );
1473  idx++;
1474  }
1475  }
1476 }
1477 
1479 {
1480  QgsStringMap map;
1481  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1482  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
1483  map[QStringLiteral( "color_type" )] = QString::number( mColorType );
1484  map[QStringLiteral( "blur_radius" )] = QString::number( mBlurRadius );
1485  map[QStringLiteral( "use_whole_shape" )] = QString::number( mUseWholeShape );
1486  map[QStringLiteral( "max_distance" )] = QString::number( mMaxDistance );
1487  map[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
1488  map[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
1489  map[QStringLiteral( "ignore_rings" )] = QString::number( mIgnoreRings );
1490  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1491  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1492  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1493  if ( mGradientRamp )
1494  {
1495  map.unite( mGradientRamp->properties() );
1496  }
1497 
1498  return map;
1499 }
1500 
1502 {
1503  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1504  if ( mGradientRamp )
1505  {
1506  sl->setColorRamp( mGradientRamp->clone() );
1507  }
1508  sl->setDistanceUnit( mDistanceUnit );
1509  sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1510  sl->setIgnoreRings( mIgnoreRings );
1511  sl->setOffset( mOffset );
1512  sl->setOffsetUnit( mOffsetUnit );
1513  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1514  copyDataDefinedProperties( sl.get() );
1515  copyPaintEffect( sl.get() );
1516  return sl.release();
1517 }
1518 
1520 {
1521  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1522  return offsetBleed;
1523 }
1524 
1526 {
1527  mDistanceUnit = unit;
1528  mOffsetUnit = unit;
1529 }
1530 
1532 {
1533  if ( mDistanceUnit == mOffsetUnit )
1534  {
1535  return mDistanceUnit;
1536  }
1538 }
1539 
1541 {
1542  mDistanceMapUnitScale = scale;
1543  mOffsetMapUnitScale = scale;
1544 }
1545 
1547 {
1548  if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1549  {
1550  return mDistanceMapUnitScale;
1551  }
1552  return QgsMapUnitScale();
1553 }
1554 
1555 
1556 //QgsImageFillSymbolLayer
1557 
1559 {
1560  setSubSymbol( new QgsLineSymbol() );
1561 }
1562 
1563 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1564 {
1565  QPainter *p = context.renderContext().painter();
1566  if ( !p )
1567  {
1568  return;
1569  }
1570 
1571  mNextAngle = mAngle;
1572  applyDataDefinedSettings( context );
1573 
1574  p->setPen( QPen( Qt::NoPen ) );
1575 
1576  QTransform bkTransform = mBrush.transform();
1578  {
1579  //transform brush to upper left corner of geometry bbox
1580  QPointF leftCorner = points.boundingRect().topLeft();
1581  QTransform t = mBrush.transform();
1582  t.translate( leftCorner.x(), leftCorner.y() );
1583  mBrush.setTransform( t );
1584  }
1585 
1586  if ( context.selected() )
1587  {
1588  QColor selColor = context.renderContext().selectionColor();
1589  // Alister - this doesn't seem to work here
1590  //if ( ! selectionIsOpaque )
1591  // selColor.setAlphaF( context.alpha() );
1592  p->setBrush( QBrush( selColor ) );
1593  _renderPolygon( p, points, rings, context );
1594  }
1595 
1596  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1597  {
1598  QTransform t = mBrush.transform();
1599  t.rotate( mNextAngle );
1600  mBrush.setTransform( t );
1601  }
1602  p->setBrush( mBrush );
1603  _renderPolygon( p, points, rings, context );
1604  if ( mStroke )
1605  {
1606  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1607  if ( rings )
1608  {
1609  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1610  for ( ; ringIt != rings->constEnd(); ++ringIt )
1611  {
1612  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1613  }
1614  }
1615  }
1616 
1617  mBrush.setTransform( bkTransform );
1618 }
1619 
1621 {
1622  if ( !symbol ) //unset current stroke
1623  {
1624  mStroke.reset( nullptr );
1625  return true;
1626  }
1627 
1628  if ( symbol->type() != QgsSymbol::Line )
1629  {
1630  delete symbol;
1631  return false;
1632  }
1633 
1634  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
1635  if ( lineSymbol )
1636  {
1637  mStroke.reset( lineSymbol );
1638  return true;
1639  }
1640 
1641  delete symbol;
1642  return false;
1643 }
1644 
1646 {
1647  mStrokeWidthUnit = unit;
1648 }
1649 
1651 {
1652  return mStrokeWidthUnit;
1653 }
1654 
1656 {
1657  mStrokeWidthMapUnitScale = scale;
1658 }
1659 
1661 {
1662  return mStrokeWidthMapUnitScale;
1663 }
1664 
1666 {
1667  if ( mStroke && mStroke->symbolLayer( 0 ) )
1668  {
1669  double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1670  return subLayerBleed;
1671  }
1672  return 0;
1673 }
1674 
1676 {
1677  double width = mStrokeWidth;
1679  {
1680  context.setOriginalValueVariable( mStrokeWidth );
1682  }
1683  return width * e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1684 }
1685 
1687 {
1688  Q_UNUSED( context )
1689  if ( !mStroke )
1690  {
1691  return QColor( Qt::black );
1692  }
1693  return mStroke->color();
1694 }
1695 
1697 {
1698  return Qt::SolidLine;
1699 #if 0
1700  if ( !mStroke )
1701  {
1702  return Qt::SolidLine;
1703  }
1704  else
1705  {
1706  return mStroke->dxfPenStyle();
1707  }
1708 #endif //0
1709 }
1710 
1711 QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1712 {
1713  QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
1714  if ( mStroke )
1715  attr.unite( mStroke->usedAttributes( context ) );
1716  return attr;
1717 }
1718 
1720 {
1722  return true;
1723  if ( mStroke && mStroke->hasDataDefinedProperties() )
1724  return true;
1725  return false;
1726 }
1727 
1728 
1729 //QgsSVGFillSymbolLayer
1730 
1731 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString &svgFilePath, double width, double angle )
1733  , mPatternWidth( width )
1734 {
1735  mStrokeWidth = 0.3;
1736  mAngle = angle;
1737  mColor = QColor( 255, 255, 255 );
1738  setSvgFilePath( svgFilePath );
1739 }
1740 
1741 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double width, double angle )
1743  , mPatternWidth( width )
1744  , mSvgData( svgData )
1745 {
1746  storeViewBox();
1747  mStrokeWidth = 0.3;
1748  mAngle = angle;
1749  mColor = QColor( 255, 255, 255 );
1750  setSubSymbol( new QgsLineSymbol() );
1751  setDefaultSvgParams();
1752 }
1753 
1755 {
1757  mPatternWidthUnit = unit;
1758  mSvgStrokeWidthUnit = unit;
1759  mStrokeWidthUnit = unit;
1760  mStroke->setOutputUnit( unit );
1761 }
1762 
1764 {
1766  if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit || mStrokeWidthUnit != unit )
1767  {
1769  }
1770  return unit;
1771 }
1772 
1774 {
1776  mPatternWidthMapUnitScale = scale;
1777  mSvgStrokeWidthMapUnitScale = scale;
1778  mStrokeWidthMapUnitScale = scale;
1779 }
1780 
1782 {
1783  if ( QgsImageFillSymbolLayer::mapUnitScale() == mPatternWidthMapUnitScale &&
1784  mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1785  mSvgStrokeWidthMapUnitScale == mStrokeWidthMapUnitScale )
1786  {
1787  return mPatternWidthMapUnitScale;
1788  }
1789  return QgsMapUnitScale();
1790 }
1791 
1792 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString &svgPath )
1793 {
1794  mSvgData = QgsApplication::svgCache()->getImageData( svgPath );
1795  storeViewBox();
1796 
1797  mSvgFilePath = svgPath;
1798  setDefaultSvgParams();
1799 }
1800 
1802 {
1803  QByteArray data;
1804  double width = 20;
1805  QString svgFilePath;
1806  double angle = 0.0;
1807 
1808  if ( properties.contains( QStringLiteral( "width" ) ) )
1809  {
1810  width = properties[QStringLiteral( "width" )].toDouble();
1811  }
1812  if ( properties.contains( QStringLiteral( "svgFile" ) ) )
1813  {
1814  svgFilePath = properties[QStringLiteral( "svgFile" )];
1815  }
1816  if ( properties.contains( QStringLiteral( "angle" ) ) )
1817  {
1818  angle = properties[QStringLiteral( "angle" )].toDouble();
1819  }
1820 
1821  std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1822  if ( !svgFilePath.isEmpty() )
1823  {
1824  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( svgFilePath, width, angle );
1825  }
1826  else
1827  {
1828  if ( properties.contains( QStringLiteral( "data" ) ) )
1829  {
1830  data = QByteArray::fromHex( properties[QStringLiteral( "data" )].toLocal8Bit() );
1831  }
1832  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( data, width, angle );
1833  }
1834 
1835  //svg parameters
1836  if ( properties.contains( QStringLiteral( "svgFillColor" ) ) )
1837  {
1838  //pre 2.5 projects used "svgFillColor"
1839  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgFillColor" )] ) );
1840  }
1841  else if ( properties.contains( QStringLiteral( "color" ) ) )
1842  {
1843  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
1844  }
1845  if ( properties.contains( QStringLiteral( "svgOutlineColor" ) ) )
1846  {
1847  //pre 2.5 projects used "svgOutlineColor"
1848  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgOutlineColor" )] ) );
1849  }
1850  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
1851  {
1852  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
1853  }
1854  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
1855  {
1856  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
1857  }
1858  if ( properties.contains( QStringLiteral( "svgOutlineWidth" ) ) )
1859  {
1860  //pre 2.5 projects used "svgOutlineWidth"
1861  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "svgOutlineWidth" )].toDouble() );
1862  }
1863  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
1864  {
1865  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
1866  }
1867  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
1868  {
1869  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
1870  }
1871 
1872  //units
1873  if ( properties.contains( QStringLiteral( "pattern_width_unit" ) ) )
1874  {
1875  symbolLayer->setPatternWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "pattern_width_unit" )] ) );
1876  }
1877  if ( properties.contains( QStringLiteral( "pattern_width_map_unit_scale" ) ) )
1878  {
1879  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "pattern_width_map_unit_scale" )] ) );
1880  }
1881  if ( properties.contains( QStringLiteral( "svg_outline_width_unit" ) ) )
1882  {
1883  symbolLayer->setSvgStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "svg_outline_width_unit" )] ) );
1884  }
1885  if ( properties.contains( QStringLiteral( "svg_outline_width_map_unit_scale" ) ) )
1886  {
1887  symbolLayer->setSvgStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "svg_outline_width_map_unit_scale" )] ) );
1888  }
1889  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
1890  {
1891  symbolLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
1892  }
1893  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1894  {
1895  symbolLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1896  }
1897 
1898  symbolLayer->restoreOldDataDefinedProperties( properties );
1899 
1900  return symbolLayer.release();
1901 }
1902 
1904 {
1905  QgsStringMap::iterator it = properties.find( QStringLiteral( "svgFile" ) );
1906  if ( it != properties.end() )
1907  {
1908  if ( saving )
1909  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1910  else
1911  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1912  }
1913 }
1914 
1916 {
1917  return QStringLiteral( "SVGFill" );
1918 }
1919 
1920 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush, const QString &svgFilePath, double patternWidth, QgsUnitTypes::RenderUnit patternWidthUnit,
1921  const QColor &svgFillColor, const QColor &svgStrokeColor, double svgStrokeWidth,
1924 {
1925  if ( mSvgViewBox.isNull() )
1926  {
1927  return;
1928  }
1929 
1930  double size = context.renderContext().convertToPainterUnits( patternWidth, patternWidthUnit, patternWidthMapUnitScale );
1931 
1932  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1933  {
1934  brush.setTextureImage( QImage() );
1935  }
1936  else
1937  {
1938  bool fitsInCache = true;
1939  double strokeWidth = context.renderContext().convertToPainterUnits( svgStrokeWidth, svgStrokeWidthUnit, svgStrokeWidthMapUnitScale );
1940  QImage patternImage = QgsApplication::svgCache()->svgAsImage( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1941  context.renderContext().scaleFactor(), fitsInCache, 0, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
1942  if ( !fitsInCache )
1943  {
1944  QPicture patternPict = QgsApplication::svgCache()->svgAsPicture( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1945  context.renderContext().scaleFactor(), false, 0, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
1946  double hwRatio = 1.0;
1947  if ( patternPict.width() > 0 )
1948  {
1949  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1950  }
1951  patternImage = QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1952  patternImage.fill( 0 ); // transparent background
1953 
1954  QPainter p( &patternImage );
1955  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1956  }
1957 
1958  QTransform brushTransform;
1959  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
1960  {
1961  QImage transparentImage = patternImage.copy();
1962  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
1963  brush.setTextureImage( transparentImage );
1964  }
1965  else
1966  {
1967  brush.setTextureImage( patternImage );
1968  }
1969  brush.setTransform( brushTransform );
1970  }
1971 }
1972 
1974 {
1975 
1976  applyPattern( mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit, mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
1977 
1978  if ( mStroke )
1979  {
1980  mStroke->startRender( context.renderContext(), context.fields() );
1981  }
1982 }
1983 
1985 {
1986  if ( mStroke )
1987  {
1988  mStroke->stopRender( context.renderContext() );
1989  }
1990 }
1991 
1993 {
1994  QgsStringMap map;
1995  if ( !mSvgFilePath.isEmpty() )
1996  {
1997  map.insert( QStringLiteral( "svgFile" ), mSvgFilePath );
1998  }
1999  else
2000  {
2001  map.insert( QStringLiteral( "data" ), QString( mSvgData.toHex() ) );
2002  }
2003 
2004  map.insert( QStringLiteral( "width" ), QString::number( mPatternWidth ) );
2005  map.insert( QStringLiteral( "angle" ), QString::number( mAngle ) );
2006 
2007  //svg parameters
2008  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2009  map.insert( QStringLiteral( "outline_color" ), QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2010  map.insert( QStringLiteral( "outline_width" ), QString::number( mSvgStrokeWidth ) );
2011 
2012  //units
2013  map.insert( QStringLiteral( "pattern_width_unit" ), QgsUnitTypes::encodeUnit( mPatternWidthUnit ) );
2014  map.insert( QStringLiteral( "pattern_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2015  map.insert( QStringLiteral( "svg_outline_width_unit" ), QgsUnitTypes::encodeUnit( mSvgStrokeWidthUnit ) );
2016  map.insert( QStringLiteral( "svg_outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSvgStrokeWidthMapUnitScale ) );
2017  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2018  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2019  return map;
2020 }
2021 
2023 {
2024  std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2025  if ( !mSvgFilePath.isEmpty() )
2026  {
2027  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth, mAngle );
2028  clonedLayer->setSvgFillColor( mColor );
2029  clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2030  clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2031  }
2032  else
2033  {
2034  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth, mAngle );
2035  }
2036 
2037  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2038  clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2039  clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2040  clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2041  clonedLayer->setStrokeWidthUnit( mStrokeWidthUnit );
2042  clonedLayer->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2043 
2044  if ( mStroke )
2045  {
2046  clonedLayer->setSubSymbol( mStroke->clone() );
2047  }
2048  copyDataDefinedProperties( clonedLayer.get() );
2049  copyPaintEffect( clonedLayer.get() );
2050  return clonedLayer.release();
2051 }
2052 
2053 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2054 {
2055  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2056  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2057  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2058  element.appendChild( symbolizerElem );
2059 
2060  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2061 
2062  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2063  symbolizerElem.appendChild( fillElem );
2064 
2065  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2066  fillElem.appendChild( graphicFillElem );
2067 
2068  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2069  graphicFillElem.appendChild( graphicElem );
2070 
2071  if ( !mSvgFilePath.isEmpty() )
2072  {
2073  // encode a parametric SVG reference
2074  double patternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2075  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mSvgStrokeWidth, mSvgStrokeWidthUnit, props );
2076  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mSvgFilePath, mColor, patternWidth, mSvgStrokeColor, strokeWidth );
2077  }
2078  else
2079  {
2080  // TODO: create svg from data
2081  // <se:InlineContent>
2082  symbolizerElem.appendChild( doc.createComment( QStringLiteral( "SVG from data not implemented yet" ) ) );
2083  }
2084 
2085  // <Rotation>
2086  QString angleFunc;
2087  bool ok;
2088  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2089  if ( !ok )
2090  {
2091  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2092  }
2093  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2094  {
2095  angleFunc = QString::number( angle + mAngle );
2096  }
2097  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2098 
2099  if ( mStroke )
2100  {
2101  // the stroke sub symbol should be stored within the Stroke element,
2102  // but it will be stored in a separated LineSymbolizer because it could
2103  // have more than one layer
2104  mStroke->toSld( doc, element, props );
2105  }
2106 }
2107 
2109 {
2110  QString path, mimeType;
2111  QColor fillColor, strokeColor;
2112  Qt::PenStyle penStyle;
2113  double size, strokeWidth;
2114 
2115  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2116  if ( fillElem.isNull() )
2117  return nullptr;
2118 
2119  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2120  if ( graphicFillElem.isNull() )
2121  return nullptr;
2122 
2123  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2124  if ( graphicElem.isNull() )
2125  return nullptr;
2126 
2127  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2128  return nullptr;
2129 
2130  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2131  return nullptr;
2132 
2133  QgsSymbolLayerUtils::lineFromSld( graphicElem, penStyle, strokeColor, strokeWidth );
2134 
2135  QString uom = element.attribute( QStringLiteral( "uom" ) );
2136  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2137  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
2138 
2139  double angle = 0.0;
2140  QString angleFunc;
2141  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2142  {
2143  bool ok;
2144  double d = angleFunc.toDouble( &ok );
2145  if ( ok )
2146  angle = d;
2147  }
2148 
2149  std::unique_ptr< QgsSVGFillSymbolLayer > sl = qgis::make_unique< QgsSVGFillSymbolLayer >( path, size, angle );
2150  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2151  sl->setSvgFillColor( fillColor );
2152  sl->setSvgStrokeColor( strokeColor );
2153  sl->setSvgStrokeWidth( strokeWidth );
2154 
2155  // try to get the stroke
2156  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2157  if ( !strokeElem.isNull() )
2158  {
2160  if ( l )
2161  {
2162  QgsSymbolLayerList layers;
2163  layers.append( l );
2164  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2165  }
2166  }
2167 
2168  return sl.release();
2169 }
2170 
2172 {
2176  {
2177  return; //no data defined settings
2178  }
2179 
2181  {
2182  context.setOriginalValueVariable( mAngle );
2184  }
2185 
2186  double width = mPatternWidth;
2188  {
2189  context.setOriginalValueVariable( mPatternWidth );
2191  }
2192  QString svgFile = mSvgFilePath;
2194  {
2195  context.setOriginalValueVariable( mSvgFilePath );
2197  context.renderContext().pathResolver() );
2198  }
2199  QColor svgFillColor = mColor;
2201  {
2204  }
2205  QColor svgStrokeColor = mSvgStrokeColor;
2207  {
2208  context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2210  }
2211  double strokeWidth = mSvgStrokeWidth;
2213  {
2214  context.setOriginalValueVariable( mSvgStrokeWidth );
2216  }
2217  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgStrokeColor, strokeWidth,
2218  mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2219 
2220 }
2221 
2222 void QgsSVGFillSymbolLayer::storeViewBox()
2223 {
2224  if ( !mSvgData.isEmpty() )
2225  {
2226  QSvgRenderer r( mSvgData );
2227  if ( r.isValid() )
2228  {
2229  mSvgViewBox = r.viewBoxF();
2230  return;
2231  }
2232  }
2233 
2234  mSvgViewBox = QRectF();
2235 }
2236 
2237 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2238 {
2239  if ( mSvgFilePath.isEmpty() )
2240  {
2241  return;
2242  }
2243 
2244  bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2245  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2246  QColor defaultFillColor, defaultStrokeColor;
2247  double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2248  QgsApplication::svgCache()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2249  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2250  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2251  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2252  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2253 
2254  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2255  double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2256 
2257  if ( hasDefaultFillColor )
2258  {
2259  mColor = defaultFillColor;
2260  mColor.setAlphaF( newFillOpacity );
2261  }
2262  if ( hasDefaultFillOpacity )
2263  {
2264  mColor.setAlphaF( defaultFillOpacity );
2265  }
2266  if ( hasDefaultStrokeColor )
2267  {
2268  mSvgStrokeColor = defaultStrokeColor;
2269  mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2270  }
2271  if ( hasDefaultStrokeOpacity )
2272  {
2273  mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2274  }
2275  if ( hasDefaultStrokeWidth )
2276  {
2277  mSvgStrokeWidth = defaultStrokeWidth;
2278  }
2279 }
2280 
2281 
2284 {
2285  setSubSymbol( new QgsLineSymbol() );
2286  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2287 }
2288 
2290 {
2291  mFillLineSymbol->setWidth( w );
2292  mLineWidth = w;
2293 }
2294 
2296 {
2297  mFillLineSymbol->setColor( c );
2298  mColor = c;
2299 }
2300 
2302 {
2303  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2304 }
2305 
2307 {
2308  delete mFillLineSymbol;
2309 }
2310 
2312 {
2313  if ( !symbol )
2314  {
2315  return false;
2316  }
2317 
2318  if ( symbol->type() == QgsSymbol::Line )
2319  {
2320  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
2321  if ( lineSymbol )
2322  {
2323  delete mFillLineSymbol;
2324  mFillLineSymbol = lineSymbol;
2325 
2326  return true;
2327  }
2328  }
2329  delete symbol;
2330  return false;
2331 }
2332 
2334 {
2335  return mFillLineSymbol;
2336 }
2337 
2339 {
2340  QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
2341  if ( mFillLineSymbol )
2342  attr.unite( mFillLineSymbol->usedAttributes( context ) );
2343  return attr;
2344 }
2345 
2347 {
2349  return true;
2350  if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2351  return true;
2352  return false;
2353 }
2354 
2356 {
2357  return 0;
2358 }
2359 
2361 {
2363  mDistanceUnit = unit;
2364  mLineWidthUnit = unit;
2365  mOffsetUnit = unit;
2366 }
2367 
2369 {
2371  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2372  {
2374  }
2375  return unit;
2376 }
2377 
2379 {
2381  mDistanceMapUnitScale = scale;
2382  mLineWidthMapUnitScale = scale;
2383  mOffsetMapUnitScale = scale;
2384 }
2385 
2387 {
2388  if ( QgsImageFillSymbolLayer::mapUnitScale() == mDistanceMapUnitScale &&
2389  mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2390  mLineWidthMapUnitScale == mOffsetMapUnitScale )
2391  {
2392  return mDistanceMapUnitScale;
2393  }
2394  return QgsMapUnitScale();
2395 }
2396 
2398 {
2399  std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2400 
2401  //default values
2402  double lineAngle = 45;
2403  double distance = 5;
2404  double lineWidth = 0.5;
2405  QColor color( Qt::black );
2406  double offset = 0.0;
2407 
2408  if ( properties.contains( QStringLiteral( "lineangle" ) ) )
2409  {
2410  //pre 2.5 projects used "lineangle"
2411  lineAngle = properties[QStringLiteral( "lineangle" )].toDouble();
2412  }
2413  else if ( properties.contains( QStringLiteral( "angle" ) ) )
2414  {
2415  lineAngle = properties[QStringLiteral( "angle" )].toDouble();
2416  }
2417  patternLayer->setLineAngle( lineAngle );
2418 
2419  if ( properties.contains( QStringLiteral( "distance" ) ) )
2420  {
2421  distance = properties[QStringLiteral( "distance" )].toDouble();
2422  }
2423  patternLayer->setDistance( distance );
2424 
2425  if ( properties.contains( QStringLiteral( "linewidth" ) ) )
2426  {
2427  //pre 2.5 projects used "linewidth"
2428  lineWidth = properties[QStringLiteral( "linewidth" )].toDouble();
2429  }
2430  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
2431  {
2432  lineWidth = properties[QStringLiteral( "outline_width" )].toDouble();
2433  }
2434  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
2435  {
2436  lineWidth = properties[QStringLiteral( "line_width" )].toDouble();
2437  }
2438  patternLayer->setLineWidth( lineWidth );
2439 
2440  if ( properties.contains( QStringLiteral( "color" ) ) )
2441  {
2442  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] );
2443  }
2444  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
2445  {
2446  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] );
2447  }
2448  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
2449  {
2450  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] );
2451  }
2452  patternLayer->setColor( color );
2453 
2454  if ( properties.contains( QStringLiteral( "offset" ) ) )
2455  {
2456  offset = properties[QStringLiteral( "offset" )].toDouble();
2457  }
2458  patternLayer->setOffset( offset );
2459 
2460 
2461  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
2462  {
2463  patternLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )] ) );
2464  }
2465  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
2466  {
2467  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )] ) );
2468  }
2469  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
2470  {
2471  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
2472  }
2473  else if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2474  {
2475  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2476  }
2477  if ( properties.contains( QStringLiteral( "line_width_map_unit_scale" ) ) )
2478  {
2479  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "line_width_map_unit_scale" )] ) );
2480  }
2481  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2482  {
2483  patternLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
2484  }
2485  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2486  {
2487  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
2488  }
2489  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2490  {
2491  patternLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2492  }
2493  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2494  {
2495  patternLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2496  }
2497 
2498  patternLayer->restoreOldDataDefinedProperties( properties );
2499 
2500  return patternLayer.release();
2501 }
2502 
2504 {
2505  return QStringLiteral( "LinePatternFill" );
2506 }
2507 
2508 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double lineAngle, double distance )
2509 {
2510  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2511 
2512  if ( !mFillLineSymbol )
2513  {
2514  return;
2515  }
2516  // We have to make a copy because marker intervals will have to be adjusted
2517  std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2518  if ( !fillLineSymbol )
2519  {
2520  return;
2521  }
2522 
2523  const QgsRenderContext &ctx = context.renderContext();
2524  //double strokePixelWidth = lineWidth * QgsSymbolLayerUtils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2525  double outputPixelDist = ctx.convertToPainterUnits( distance, mDistanceUnit, mDistanceMapUnitScale );
2526  double outputPixelOffset = ctx.convertToPainterUnits( mOffset, mOffsetUnit, mOffsetMapUnitScale );
2527 
2528  // NOTE: this may need to be modified if we ever change from a forced rasterized/brush approach,
2529  // because potentially we may want to allow vector based line pattern fills where the first line
2530  // is offset by a large distance
2531 
2532  // fix truncated pattern with larger offsets
2533  outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2534  if ( outputPixelOffset > outputPixelDist / 2.0 )
2535  outputPixelOffset -= outputPixelDist;
2536 
2537  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2538  // For marker lines we have to get markers interval.
2539  double outputPixelBleed = 0;
2540  double outputPixelInterval = 0; // maximum interval
2541  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2542  {
2543  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2544  double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() );
2545  outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2546 
2547  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2548  if ( markerLineLayer )
2549  {
2550  double outputPixelLayerInterval = ctx.convertToPainterUnits( markerLineLayer->interval(), markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2551 
2552  // There may be multiple marker lines with different intervals.
2553  // In theory we should find the least common multiple, but that could be too
2554  // big (multiplication of intervals in the worst case).
2555  // Because patterns without small common interval would look strange, we
2556  // believe that the longest interval should usually be sufficient.
2557  outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2558  }
2559  }
2560 
2561  if ( outputPixelInterval > 0 )
2562  {
2563  // We have to adjust marker intervals to integer pixel size to get
2564  // repeatable pattern.
2565  double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2566  outputPixelInterval = std::round( outputPixelInterval );
2567 
2568  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2569  {
2570  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2571 
2572  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2573  if ( markerLineLayer )
2574  {
2575  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2576  }
2577  }
2578  }
2579 
2580  //create image
2581  int height, width;
2582  lineAngle = std::fmod( lineAngle, 360 );
2583  if ( lineAngle < 0 )
2584  lineAngle += 360;
2585  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2586  {
2587  height = outputPixelDist;
2588  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2589  }
2590  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2591  {
2592  width = outputPixelDist;
2593  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2594  }
2595  else
2596  {
2597  height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2598  width = outputPixelDist / std::sin( lineAngle * M_PI / 180 );
2599 
2600  // recalculate real angle and distance after rounding to pixels
2601  lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2602  if ( lineAngle < 0 )
2603  {
2604  lineAngle += 360.;
2605  }
2606 
2607  height = std::abs( height );
2608  width = std::abs( width );
2609 
2610  outputPixelDist = std::abs( height * std::cos( lineAngle * M_PI / 180 ) );
2611 
2612  // Round offset to correspond to one pixel height, otherwise lines may
2613  // be shifted on tile border if offset falls close to pixel center
2614  int offsetHeight = static_cast< int >( std::round( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) );
2615  outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 );
2616  }
2617 
2618  //depending on the angle, we might need to render into a larger image and use a subset of it
2619  double dx = 0;
2620  double dy = 0;
2621 
2622  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2623  // thus we add integer multiplications of width and height covering the bleed
2624  int bufferMulti = static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2625 
2626  // Always buffer at least once so that center of line marker in upper right corner
2627  // does not fall outside due to representation error
2628  bufferMulti = std::max( bufferMulti, 1 );
2629 
2630  int xBuffer = width * bufferMulti;
2631  int yBuffer = height * bufferMulti;
2632  int innerWidth = width;
2633  int innerHeight = height;
2634  width += 2 * xBuffer;
2635  height += 2 * yBuffer;
2636 
2637  //protect from zero width/height image and symbol layer from eating too much memory
2638  if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2639  {
2640  return;
2641  }
2642 
2643  QImage patternImage( width, height, QImage::Format_ARGB32 );
2644  patternImage.fill( 0 );
2645 
2646  QPointF p1, p2, p3, p4, p5, p6;
2647  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2648  {
2649  p1 = QPointF( 0, yBuffer );
2650  p2 = QPointF( width, yBuffer );
2651  p3 = QPointF( 0, yBuffer + innerHeight );
2652  p4 = QPointF( width, yBuffer + innerHeight );
2653  }
2654  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2655  {
2656  p1 = QPointF( xBuffer, height );
2657  p2 = QPointF( xBuffer, 0 );
2658  p3 = QPointF( xBuffer + innerWidth, height );
2659  p4 = QPointF( xBuffer + innerWidth, 0 );
2660  }
2661  else if ( lineAngle > 0 && lineAngle < 90 )
2662  {
2663  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2664  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2665  p1 = QPointF( 0, height );
2666  p2 = QPointF( width, 0 );
2667  p3 = QPointF( -dx, height - dy );
2668  p4 = QPointF( width - dx, -dy );
2669  p5 = QPointF( dx, height + dy );
2670  p6 = QPointF( width + dx, dy );
2671  }
2672  else if ( lineAngle > 180 && lineAngle < 270 )
2673  {
2674  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2675  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2676  p1 = QPointF( width, 0 );
2677  p2 = QPointF( 0, height );
2678  p3 = QPointF( width - dx, -dy );
2679  p4 = QPointF( -dx, height - dy );
2680  p5 = QPointF( width + dx, dy );
2681  p6 = QPointF( dx, height + dy );
2682  }
2683  else if ( lineAngle > 90 && lineAngle < 180 )
2684  {
2685  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2686  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2687  p1 = QPointF( 0, 0 );
2688  p2 = QPointF( width, height );
2689  p5 = QPointF( dx, -dy );
2690  p6 = QPointF( width + dx, height - dy );
2691  p3 = QPointF( -dx, dy );
2692  p4 = QPointF( width - dx, height + dy );
2693  }
2694  else if ( lineAngle > 270 && lineAngle < 360 )
2695  {
2696  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2697  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2698  p1 = QPointF( width, height );
2699  p2 = QPointF( 0, 0 );
2700  p5 = QPointF( width + dx, height - dy );
2701  p6 = QPointF( dx, -dy );
2702  p3 = QPointF( width - dx, height + dy );
2703  p4 = QPointF( -dx, dy );
2704  }
2705 
2706  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2707  {
2708  QPointF tempPt;
2709  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2710  p3 = QPointF( tempPt.x(), tempPt.y() );
2711  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2712  p4 = QPointF( tempPt.x(), tempPt.y() );
2713  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2714  p5 = QPointF( tempPt.x(), tempPt.y() );
2715  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2716  p6 = QPointF( tempPt.x(), tempPt.y() );
2717 
2718  //update p1, p2 last
2719  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2720  p1 = QPointF( tempPt.x(), tempPt.y() );
2721  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2722  p2 = QPointF( tempPt.x(), tempPt.y() );
2723  }
2724 
2725  QPainter p( &patternImage );
2726 
2727 #if 0
2728  // DEBUG: Draw rectangle
2729  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2730  QPen pen( QColor( Qt::black ) );
2731  pen.setWidthF( 0.1 );
2732  pen.setCapStyle( Qt::FlatCap );
2733  p.setPen( pen );
2734 
2735  // To see this rectangle, comment buffer cut below.
2736  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2737  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2738  p.drawPolygon( polygon );
2739 
2740  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 );
2741  p.drawPolygon( polygon );
2742 #endif
2743 
2744  // Use antialiasing because without antialiasing lines are rendered to the
2745  // right and below the mathematically defined points (not symmetrical)
2746  // and such tiles become useless for are filling
2747  p.setRenderHint( QPainter::Antialiasing, true );
2748 
2749  // line rendering needs context for drawing on patternImage
2750  QgsRenderContext lineRenderContext;
2751  lineRenderContext.setPainter( &p );
2752  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
2754  lineRenderContext.setMapToPixel( mtp );
2755  lineRenderContext.setForceVectorOutput( false );
2756  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2757 
2758  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2759 
2760  QVector<QPolygonF> polygons;
2761  polygons.append( QPolygonF() << p1 << p2 );
2762  polygons.append( QPolygonF() << p3 << p4 );
2763  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2764  {
2765  polygons.append( QPolygonF() << p5 << p6 );
2766  }
2767 
2768  for ( const QPolygonF &polygon : qgis::as_const( polygons ) )
2769  {
2770  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2771  }
2772 
2773  fillLineSymbol->stopRender( lineRenderContext );
2774  p.end();
2775 
2776  // Cut off the buffer
2777  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2778 
2779  //set image to mBrush
2780  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2781  {
2782  QImage transparentImage = patternImage.copy();
2783  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2784  brush.setTextureImage( transparentImage );
2785  }
2786  else
2787  {
2788  brush.setTextureImage( patternImage );
2789  }
2790 
2791  QTransform brushTransform;
2792  brush.setTransform( brushTransform );
2793 }
2794 
2796 {
2797  applyPattern( context, mBrush, mLineAngle, mDistance );
2798 
2799  if ( mFillLineSymbol )
2800  {
2801  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2802  }
2803 }
2804 
2806 {
2807  if ( mFillLineSymbol )
2808  {
2809  mFillLineSymbol->stopRender( context.renderContext() );
2810  }
2811 }
2812 
2814 {
2815  QgsStringMap map;
2816  map.insert( QStringLiteral( "angle" ), QString::number( mLineAngle ) );
2817  map.insert( QStringLiteral( "distance" ), QString::number( mDistance ) );
2818  map.insert( QStringLiteral( "line_width" ), QString::number( mLineWidth ) );
2819  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2820  map.insert( QStringLiteral( "offset" ), QString::number( mOffset ) );
2821  map.insert( QStringLiteral( "distance_unit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
2822  map.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineWidthUnit ) );
2823  map.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
2824  map.insert( QStringLiteral( "distance_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2825  map.insert( QStringLiteral( "line_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2826  map.insert( QStringLiteral( "offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2827  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2828  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2829  return map;
2830 }
2831 
2833 {
2835  if ( mFillLineSymbol )
2836  {
2837  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2838  }
2839  copyPaintEffect( clonedLayer );
2840  copyDataDefinedProperties( clonedLayer );
2841  return clonedLayer;
2842 }
2843 
2844 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2845 {
2846  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2847  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2848  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2849  element.appendChild( symbolizerElem );
2850 
2851  // <Geometry>
2852  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2853 
2854  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2855  symbolizerElem.appendChild( fillElem );
2856 
2857  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2858  fillElem.appendChild( graphicFillElem );
2859 
2860  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2861  graphicFillElem.appendChild( graphicElem );
2862 
2863  //line properties must be inside the graphic definition
2864  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2865  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2866  lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2867  double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
2868  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "horline" ), QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2869 
2870  // <Rotation>
2871  QString angleFunc;
2872  bool ok;
2873  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2874  if ( !ok )
2875  {
2876  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mLineAngle );
2877  }
2878  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2879  {
2880  angleFunc = QString::number( angle + mLineAngle );
2881  }
2882  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2883 
2884  // <se:Displacement>
2885  QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2886  lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
2887  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
2888 }
2889 
2890 QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFactor ) const
2891 {
2892  QString featureStyle;
2893  featureStyle.append( "Brush(" );
2894  featureStyle.append( QStringLiteral( "fc:%1" ).arg( mColor.name() ) );
2895  featureStyle.append( QStringLiteral( ",bc:%1" ).arg( QStringLiteral( "#00000000" ) ) ); //transparent background
2896  featureStyle.append( ",id:\"ogr-brush-2\"" );
2897  featureStyle.append( QStringLiteral( ",a:%1" ).arg( mLineAngle ) );
2898  featureStyle.append( QStringLiteral( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2899  featureStyle.append( ",dx:0mm" );
2900  featureStyle.append( QStringLiteral( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2901  featureStyle.append( ')' );
2902  return featureStyle;
2903 }
2904 
2906 {
2908  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2909  {
2910  return; //no data defined settings
2911  }
2912 
2913  double lineAngle = mLineAngle;
2915  {
2916  context.setOriginalValueVariable( mLineAngle );
2918  }
2919  double distance = mDistance;
2921  {
2922  context.setOriginalValueVariable( mDistance );
2924  }
2925  applyPattern( context, mBrush, lineAngle, distance );
2926 }
2927 
2929 {
2930  QString name;
2931  QColor fillColor, lineColor;
2932  double size, lineWidth;
2933  Qt::PenStyle lineStyle;
2934 
2935  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2936  if ( fillElem.isNull() )
2937  return nullptr;
2938 
2939  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2940  if ( graphicFillElem.isNull() )
2941  return nullptr;
2942 
2943  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2944  if ( graphicElem.isNull() )
2945  return nullptr;
2946 
2947  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2948  return nullptr;
2949 
2950  if ( name != QLatin1String( "horline" ) )
2951  return nullptr;
2952 
2953  double angle = 0.0;
2954  QString angleFunc;
2955  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2956  {
2957  bool ok;
2958  double d = angleFunc.toDouble( &ok );
2959  if ( ok )
2960  angle = d;
2961  }
2962 
2963  double offset = 0.0;
2964  QPointF vectOffset;
2965  if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) )
2966  {
2967  offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
2968  }
2969 
2970  QString uom = element.attribute( QStringLiteral( "uom" ) );
2971  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2972  lineWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, lineWidth );
2973 
2974  std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2975  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2976  sl->setColor( lineColor );
2977  sl->setLineWidth( lineWidth );
2978  sl->setLineAngle( angle );
2979  sl->setOffset( offset );
2980  sl->setDistance( size );
2981 
2982  // try to get the stroke
2983  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2984  if ( !strokeElem.isNull() )
2985  {
2987  if ( l )
2988  {
2989  QgsSymbolLayerList layers;
2990  layers.append( l );
2991  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2992  }
2993  }
2994 
2995  return sl.release();
2996 }
2997 
2998 
3000 
3003 {
3004  mDistanceX = 15;
3005  mDistanceY = 15;
3006  mDisplacementX = 0;
3007  mDisplacementY = 0;
3008  mOffsetX = 0;
3009  mOffsetY = 0;
3010  setSubSymbol( new QgsMarkerSymbol() );
3011  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
3012 }
3013 
3015 {
3016  delete mMarkerSymbol;
3017 }
3018 
3020 {
3022  mDistanceXUnit = unit;
3023  mDistanceYUnit = unit;
3024  mDisplacementXUnit = unit;
3025  mDisplacementYUnit = unit;
3026  mOffsetXUnit = unit;
3027  mOffsetYUnit = unit;
3028  if ( mMarkerSymbol )
3029  {
3030  mMarkerSymbol->setOutputUnit( unit );
3031  }
3032 
3033 }
3034 
3036 {
3038  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit || mOffsetXUnit != unit || mOffsetYUnit != unit )
3039  {
3041  }
3042  return unit;
3043 }
3044 
3046 {
3048  mDistanceXMapUnitScale = scale;
3049  mDistanceYMapUnitScale = scale;
3052  mOffsetXMapUnitScale = scale;
3053  mOffsetYMapUnitScale = scale;
3054 }
3055 
3057 {
3064  {
3065  return mDistanceXMapUnitScale;
3066  }
3067  return QgsMapUnitScale();
3068 }
3069 
3071 {
3072  std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = qgis::make_unique< QgsPointPatternFillSymbolLayer >();
3073  if ( properties.contains( QStringLiteral( "distance_x" ) ) )
3074  {
3075  layer->setDistanceX( properties[QStringLiteral( "distance_x" )].toDouble() );
3076  }
3077  if ( properties.contains( QStringLiteral( "distance_y" ) ) )
3078  {
3079  layer->setDistanceY( properties[QStringLiteral( "distance_y" )].toDouble() );
3080  }
3081  if ( properties.contains( QStringLiteral( "displacement_x" ) ) )
3082  {
3083  layer->setDisplacementX( properties[QStringLiteral( "displacement_x" )].toDouble() );
3084  }
3085  if ( properties.contains( QStringLiteral( "displacement_y" ) ) )
3086  {
3087  layer->setDisplacementY( properties[QStringLiteral( "displacement_y" )].toDouble() );
3088  }
3089  if ( properties.contains( QStringLiteral( "offset_x" ) ) )
3090  {
3091  layer->setOffsetX( properties[QStringLiteral( "offset_x" )].toDouble() );
3092  }
3093  if ( properties.contains( QStringLiteral( "offset_y" ) ) )
3094  {
3095  layer->setOffsetY( properties[QStringLiteral( "offset_y" )].toDouble() );
3096  }
3097 
3098  if ( properties.contains( QStringLiteral( "distance_x_unit" ) ) )
3099  {
3100  layer->setDistanceXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_x_unit" )] ) );
3101  }
3102  if ( properties.contains( QStringLiteral( "distance_x_map_unit_scale" ) ) )
3103  {
3104  layer->setDistanceXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_x_map_unit_scale" )] ) );
3105  }
3106  if ( properties.contains( QStringLiteral( "distance_y_unit" ) ) )
3107  {
3108  layer->setDistanceYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_y_unit" )] ) );
3109  }
3110  if ( properties.contains( QStringLiteral( "distance_y_map_unit_scale" ) ) )
3111  {
3112  layer->setDistanceYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_y_map_unit_scale" )] ) );
3113  }
3114  if ( properties.contains( QStringLiteral( "displacement_x_unit" ) ) )
3115  {
3116  layer->setDisplacementXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_x_unit" )] ) );
3117  }
3118  if ( properties.contains( QStringLiteral( "displacement_x_map_unit_scale" ) ) )
3119  {
3120  layer->setDisplacementXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_x_map_unit_scale" )] ) );
3121  }
3122  if ( properties.contains( QStringLiteral( "displacement_y_unit" ) ) )
3123  {
3124  layer->setDisplacementYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_y_unit" )] ) );
3125  }
3126  if ( properties.contains( QStringLiteral( "displacement_y_map_unit_scale" ) ) )
3127  {
3128  layer->setDisplacementYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_y_map_unit_scale" )] ) );
3129  }
3130  if ( properties.contains( QStringLiteral( "offset_x_unit" ) ) )
3131  {
3132  layer->setOffsetXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_x_unit" )] ) );
3133  }
3134  if ( properties.contains( QStringLiteral( "offset_x_map_unit_scale" ) ) )
3135  {
3136  layer->setOffsetXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_x_map_unit_scale" )] ) );
3137  }
3138  if ( properties.contains( QStringLiteral( "offset_y_unit" ) ) )
3139  {
3140  layer->setOffsetYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_y_unit" )] ) );
3141  }
3142  if ( properties.contains( QStringLiteral( "offset_y_map_unit_scale" ) ) )
3143  {
3144  layer->setOffsetYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_y_map_unit_scale" )] ) );
3145  }
3146 
3147  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
3148  {
3149  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
3150  }
3151  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3152  {
3153  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3154  }
3155 
3156  layer->restoreOldDataDefinedProperties( properties );
3157 
3158  return layer.release();
3159 }
3160 
3162 {
3163  return QStringLiteral( "PointPatternFill" );
3164 }
3165 
3166 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double distanceX, double distanceY,
3167  double displacementX, double displacementY, double offsetX, double offsetY )
3168 {
3169  //render 3 rows and columns in one go to easily incorporate displacement
3170  const QgsRenderContext &ctx = context.renderContext();
3173 
3174  double widthOffset = std::fmod( ctx.convertToPainterUnits( offsetX, mOffsetXUnit, mOffsetXMapUnitScale ), width );
3175  double heightOffset = std::fmod( ctx.convertToPainterUnits( offsetY, mOffsetYUnit, mOffsetYMapUnitScale ), height );
3176 
3177  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3178  {
3179  QImage img;
3180  brush.setTextureImage( img );
3181  return;
3182  }
3183 
3184  QImage patternImage( width, height, QImage::Format_ARGB32 );
3185  patternImage.fill( 0 );
3186  if ( patternImage.isNull() )
3187  {
3188  brush.setTextureImage( QImage() );
3189  return;
3190  }
3191  if ( mMarkerSymbol )
3192  {
3193  QPainter p( &patternImage );
3194 
3195  //marker rendering needs context for drawing on patternImage
3196  QgsRenderContext pointRenderContext;
3197  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3198  pointRenderContext.setPainter( &p );
3199  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
3201  {
3202  pointRenderContext.setFlag( QgsRenderContext::Antialiasing, true );
3203  p.setRenderHint( QPainter::Antialiasing, true );
3204  }
3206  pointRenderContext.setMapToPixel( mtp );
3207  pointRenderContext.setForceVectorOutput( false );
3208  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3209 
3210  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3211 
3212  //render points on distance grid
3213  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3214  {
3215  for ( double currentY = -height; currentY <= height * 2.0; currentY += height )
3216  {
3217  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.feature(), pointRenderContext );
3218  }
3219  }
3220 
3221  //render displaced points
3224  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3225  {
3226  for ( double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3227  {
3228  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.feature(), pointRenderContext );
3229  }
3230  }
3231 
3232  for ( double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3233  {
3234  for ( double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3235  {
3236  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3237  }
3238  }
3239 
3240  mMarkerSymbol->stopRender( pointRenderContext );
3241  }
3242 
3243  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
3244  {
3245  QImage transparentImage = patternImage.copy();
3246  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
3247  brush.setTextureImage( transparentImage );
3248  }
3249  else
3250  {
3251  brush.setTextureImage( patternImage );
3252  }
3253  QTransform brushTransform;
3254  brush.setTransform( brushTransform );
3255 }
3256 
3258 {
3260 
3261  if ( mStroke )
3262  {
3263  mStroke->startRender( context.renderContext(), context.fields() );
3264  }
3265 }
3266 
3268 {
3269  if ( mStroke )
3270  {
3271  mStroke->stopRender( context.renderContext() );
3272  }
3273 }
3274 
3276 {
3277  QgsStringMap map;
3278  map.insert( QStringLiteral( "distance_x" ), QString::number( mDistanceX ) );
3279  map.insert( QStringLiteral( "distance_y" ), QString::number( mDistanceY ) );
3280  map.insert( QStringLiteral( "displacement_x" ), QString::number( mDisplacementX ) );
3281  map.insert( QStringLiteral( "displacement_y" ), QString::number( mDisplacementY ) );
3282  map.insert( QStringLiteral( "offset_x" ), QString::number( mOffsetX ) );
3283  map.insert( QStringLiteral( "offset_y" ), QString::number( mOffsetY ) );
3284  map.insert( QStringLiteral( "distance_x_unit" ), QgsUnitTypes::encodeUnit( mDistanceXUnit ) );
3285  map.insert( QStringLiteral( "distance_y_unit" ), QgsUnitTypes::encodeUnit( mDistanceYUnit ) );
3286  map.insert( QStringLiteral( "displacement_x_unit" ), QgsUnitTypes::encodeUnit( mDisplacementXUnit ) );
3287  map.insert( QStringLiteral( "displacement_y_unit" ), QgsUnitTypes::encodeUnit( mDisplacementYUnit ) );
3288  map.insert( QStringLiteral( "offset_x_unit" ), QgsUnitTypes::encodeUnit( mOffsetXUnit ) );
3289  map.insert( QStringLiteral( "offset_y_unit" ), QgsUnitTypes::encodeUnit( mOffsetYUnit ) );
3290  map.insert( QStringLiteral( "distance_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3291  map.insert( QStringLiteral( "distance_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3292  map.insert( QStringLiteral( "displacement_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3293  map.insert( QStringLiteral( "displacement_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3294  map.insert( QStringLiteral( "offset_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetXMapUnitScale ) );
3295  map.insert( QStringLiteral( "offset_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetYMapUnitScale ) );
3296  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
3297  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
3298  return map;
3299 }
3300 
3302 {
3304  if ( mMarkerSymbol )
3305  {
3306  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3307  }
3308  copyDataDefinedProperties( clonedLayer );
3309  copyPaintEffect( clonedLayer );
3310  return clonedLayer;
3311 }
3312 
3313 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3314 {
3315  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3316  {
3317  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
3318  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
3319  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
3320  element.appendChild( symbolizerElem );
3321 
3322  // <Geometry>
3323  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
3324 
3325  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
3326  symbolizerElem.appendChild( fillElem );
3327 
3328  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
3329  fillElem.appendChild( graphicFillElem );
3330 
3331  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3334  QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
3335  QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "distance" ), dist );
3336  symbolizerElem.appendChild( distanceElem );
3337 
3338  QgsSymbolLayer *layer = mMarkerSymbol->symbolLayer( i );
3339  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
3340  if ( !markerLayer )
3341  {
3342  QString errorMsg = QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3343  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3344  }
3345  else
3346  {
3347  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3348  }
3349  }
3350 }
3351 
3353 {
3354  Q_UNUSED( element )
3355  return nullptr;
3356 }
3357 
3359 {
3360  if ( !symbol )
3361  {
3362  return false;
3363  }
3364 
3365  if ( symbol->type() == QgsSymbol::Marker )
3366  {
3367  QgsMarkerSymbol *markerSymbol = static_cast<QgsMarkerSymbol *>( symbol );
3368  delete mMarkerSymbol;
3369  mMarkerSymbol = markerSymbol;
3370  }
3371  return true;
3372 }
3373 
3375 {
3379  {
3380  return;
3381  }
3382 
3383  double distanceX = mDistanceX;
3385  {
3388  }
3389  double distanceY = mDistanceY;
3391  {
3394  }
3395  double displacementX = mDisplacementX;
3397  {
3400  }
3401  double displacementY = mDisplacementY;
3403  {
3406  }
3407  double offsetX = mOffsetX;
3409  {
3412  }
3413  double offsetY = mOffsetY;
3415  {
3418  }
3419  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY, offsetX, offsetY );
3420 }
3421 
3423 {
3424  return 0;
3425 }
3426 
3428 {
3429  QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes( context );
3430 
3431  if ( mMarkerSymbol )
3432  attributes.unite( mMarkerSymbol->usedAttributes( context ) );
3433 
3434  return attributes;
3435 }
3436 
3438 {
3440  return true;
3442  return true;
3443  return false;
3444 }
3445 
3447 {
3448  mColor = c;
3449  if ( mMarkerSymbol )
3450  mMarkerSymbol->setColor( c );
3451 }
3452 
3454 {
3455  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3456 }
3457 
3459 
3460 
3462 {
3463  setSubSymbol( new QgsMarkerSymbol() );
3464 }
3465 
3467 {
3468  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3469 
3470  if ( properties.contains( QStringLiteral( "point_on_surface" ) ) )
3471  sl->setPointOnSurface( properties[QStringLiteral( "point_on_surface" )].toInt() != 0 );
3472  if ( properties.contains( QStringLiteral( "point_on_all_parts" ) ) )
3473  sl->setPointOnAllParts( properties[QStringLiteral( "point_on_all_parts" )].toInt() != 0 );
3474 
3475  sl->restoreOldDataDefinedProperties( properties );
3476 
3477  return sl.release();
3478 }
3479 
3481 {
3482  return QStringLiteral( "CentroidFill" );
3483 }
3484 
3486 {
3487  mMarker->setColor( color );
3488  mColor = color;
3489 }
3490 
3492 {
3493  return mMarker ? mMarker->color() : mColor;
3494 }
3495 
3497 {
3498  mMarker->setOpacity( context.opacity() );
3499  mMarker->startRender( context.renderContext(), context.fields() );
3500 
3501  mCurrentFeatureId = -1;
3502  mBiggestPartIndex = 0;
3503 }
3504 
3506 {
3507  mMarker->stopRender( context.renderContext() );
3508 }
3509 
3510 void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3511 {
3512  if ( !mPointOnAllParts )
3513  {
3514  const QgsFeature *feature = context.feature();
3515  if ( feature )
3516  {
3517  if ( feature->id() != mCurrentFeatureId )
3518  {
3519  mCurrentFeatureId = feature->id();
3520  mBiggestPartIndex = 1;
3521 
3522  if ( context.geometryPartCount() > 1 )
3523  {
3524  const QgsGeometry geom = feature->geometry();
3525  const QgsGeometryCollection *geomCollection = static_cast<const QgsGeometryCollection *>( geom.constGet() );
3526 
3527  double area = 0;
3528  double areaBiggest = 0;
3529  for ( int i = 0; i < context.geometryPartCount(); ++i )
3530  {
3531  area = geomCollection->geometryN( i )->area();
3532  if ( area > areaBiggest )
3533  {
3534  areaBiggest = area;
3535  mBiggestPartIndex = i + 1;
3536  }
3537  }
3538  }
3539  }
3540  }
3541  }
3542 
3543  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3544  {
3545  QPointF centroid = mPointOnSurface ? QgsSymbolLayerUtils::polygonPointOnSurface( points, rings ) : QgsSymbolLayerUtils::polygonCentroid( points );
3546  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3547  }
3548 }
3549 
3551 {
3552  QgsStringMap map;
3553  map[QStringLiteral( "point_on_surface" )] = QString::number( mPointOnSurface );
3554  map[QStringLiteral( "point_on_all_parts" )] = QString::number( mPointOnAllParts );
3555  return map;
3556 }
3557 
3559 {
3560  std::unique_ptr< QgsCentroidFillSymbolLayer > x = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3561  x->mAngle = mAngle;
3562  x->mColor = mColor;
3563  x->setSubSymbol( mMarker->clone() );
3564  x->setPointOnSurface( mPointOnSurface );
3565  x->setPointOnAllParts( mPointOnAllParts );
3566  copyDataDefinedProperties( x.get() );
3567  copyPaintEffect( x.get() );
3568  return x.release();
3569 }
3570 
3571 void QgsCentroidFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3572 {
3573  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3574  // used with PointSymbolizer, then the semantic is to use the centroid
3575  // of the geometry, or any similar representative point.
3576  mMarker->toSld( doc, element, props );
3577 }
3578 
3580 {
3582  if ( !l )
3583  return nullptr;
3584 
3585  QgsSymbolLayerList layers;
3586  layers.append( l );
3587  std::unique_ptr< QgsMarkerSymbol > marker( new QgsMarkerSymbol( layers ) );
3588 
3589  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3590  sl->setSubSymbol( marker.release() );
3591  sl->setPointOnAllParts( false );
3592  return sl.release();
3593 }
3594 
3595 
3597 {
3598  return mMarker.get();
3599 }
3600 
3602 {
3603  if ( !symbol || symbol->type() != QgsSymbol::Marker )
3604  {
3605  delete symbol;
3606  return false;
3607  }
3608 
3609  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
3610  mColor = mMarker->color();
3611  return true;
3612 }
3613 
3615 {
3616  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
3617 
3618  if ( mMarker )
3619  attributes.unite( mMarker->usedAttributes( context ) );
3620 
3621  return attributes;
3622 }
3623 
3625 {
3627  return true;
3628  if ( mMarker && mMarker->hasDataDefinedProperties() )
3629  return true;
3630  return false;
3631 }
3632 
3634 {
3635  if ( mMarker )
3636  {
3637  mMarker->setOutputUnit( unit );
3638  }
3639 }
3640 
3642 {
3643  if ( mMarker )
3644  {
3645  return mMarker->outputUnit();
3646  }
3647  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
3648 }
3649 
3651 {
3652  if ( mMarker )
3653  {
3654  mMarker->setMapUnitScale( scale );
3655  }
3656 }
3657 
3659 {
3660  if ( mMarker )
3661  {
3662  return mMarker->mapUnitScale();
3663  }
3664  return QgsMapUnitScale();
3665 }
3666 
3667 
3668 
3669 
3672  , mImageFilePath( imageFilePath )
3673 {
3674  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3675 }
3676 
3678 {
3680  double alpha = 1.0;
3681  QPointF offset;
3682  double angle = 0.0;
3683  double width = 0.0;
3684 
3685  QString imagePath;
3686  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3687  {
3688  imagePath = properties[QStringLiteral( "imageFile" )];
3689  }
3690  if ( properties.contains( QStringLiteral( "coordinate_mode" ) ) )
3691  {
3692  mode = static_cast< FillCoordinateMode >( properties[QStringLiteral( "coordinate_mode" )].toInt() );
3693  }
3694  if ( properties.contains( QStringLiteral( "alpha" ) ) )
3695  {
3696  alpha = properties[QStringLiteral( "alpha" )].toDouble();
3697  }
3698  if ( properties.contains( QStringLiteral( "offset" ) ) )
3699  {
3700  offset = QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] );
3701  }
3702  if ( properties.contains( QStringLiteral( "angle" ) ) )
3703  {
3704  angle = properties[QStringLiteral( "angle" )].toDouble();
3705  }
3706  if ( properties.contains( QStringLiteral( "width" ) ) )
3707  {
3708  width = properties[QStringLiteral( "width" )].toDouble();
3709  }
3710  std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = qgis::make_unique< QgsRasterFillSymbolLayer >( imagePath );
3711  symbolLayer->setCoordinateMode( mode );
3712  symbolLayer->setOpacity( alpha );
3713  symbolLayer->setOffset( offset );
3714  symbolLayer->setAngle( angle );
3715  symbolLayer->setWidth( width );
3716  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3717  {
3718  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
3719  }
3720  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3721  {
3722  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
3723  }
3724  if ( properties.contains( QStringLiteral( "width_unit" ) ) )
3725  {
3726  symbolLayer->setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "width_unit" )] ) );
3727  }
3728  if ( properties.contains( QStringLiteral( "width_map_unit_scale" ) ) )
3729  {
3730  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "width_map_unit_scale" )] ) );
3731  }
3732 
3733  symbolLayer->restoreOldDataDefinedProperties( properties );
3734 
3735  return symbolLayer.release();
3736 }
3737 
3739 {
3740  QgsStringMap::iterator it = properties.find( QStringLiteral( "imageFile" ) );
3741  if ( it != properties.end() )
3742  {
3743  if ( saving )
3744  it.value() = pathResolver.writePath( it.value() );
3745  else
3746  it.value() = pathResolver.readPath( it.value() );
3747  }
3748 }
3749 
3751 {
3752  Q_UNUSED( symbol )
3753  return true;
3754 }
3755 
3757 {
3758  return QStringLiteral( "RasterFill" );
3759 }
3760 
3761 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3762 {
3763  QPainter *p = context.renderContext().painter();
3764  if ( !p )
3765  {
3766  return;
3767  }
3768 
3769  QPointF offset;
3770  if ( !mOffset.isNull() )
3771  {
3772  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
3773  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
3774  p->translate( offset );
3775  }
3776  if ( mCoordinateMode == Feature )
3777  {
3778  QRectF boundingRect = points.boundingRect();
3779  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3780  boundingRect.top() - mBrush.transform().dy() ) );
3781  }
3782 
3783  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3784  if ( !mOffset.isNull() )
3785  {
3786  p->translate( -offset );
3787  }
3788 }
3789 
3791 {
3792  applyPattern( mBrush, mImageFilePath, mWidth, mOpacity, context );
3793 }
3794 
3796 {
3797  Q_UNUSED( context )
3798 }
3799 
3801 {
3802  QgsStringMap map;
3803  map[QStringLiteral( "imageFile" )] = mImageFilePath;
3804  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
3805  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3806  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3807  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3808  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3809  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3810  map[QStringLiteral( "width" )] = QString::number( mWidth );
3811  map[QStringLiteral( "width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
3812  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
3813  return map;
3814 }
3815 
3817 {
3818  std::unique_ptr< QgsRasterFillSymbolLayer > sl = qgis::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
3819  sl->setCoordinateMode( mCoordinateMode );
3820  sl->setOpacity( mOpacity );
3821  sl->setOffset( mOffset );
3822  sl->setOffsetUnit( mOffsetUnit );
3823  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
3824  sl->setAngle( mAngle );
3825  sl->setWidth( mWidth );
3826  sl->setWidthUnit( mWidthUnit );
3827  sl->setWidthMapUnitScale( mWidthMapUnitScale );
3828  copyDataDefinedProperties( sl.get() );
3829  copyPaintEffect( sl.get() );
3830  return sl.release();
3831 }
3832 
3834 {
3835  return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
3836 }
3837 
3838 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
3839 {
3840  mImageFilePath = imagePath;
3841 }
3842 
3844 {
3845  mCoordinateMode = mode;
3846 }
3847 
3849 {
3850  mOpacity = opacity;
3851 }
3852 
3854 {
3855  if ( !dataDefinedProperties().hasActiveProperties() )
3856  return; // shortcut
3857 
3858  bool hasWidthExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
3859  bool hasFileExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFile );
3860  bool hasOpacityExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity );
3861  bool hasAngleExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
3862 
3863  if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
3864  {
3865  return; //no data defined settings
3866  }
3867 
3868  bool ok;
3869  if ( hasAngleExpression )
3870  {
3871  context.setOriginalValueVariable( mAngle );
3873  if ( ok )
3874  mNextAngle = nextAngle;
3875  }
3876 
3877  if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
3878  {
3879  return; //nothing further to do
3880  }
3881 
3882  double width = mWidth;
3883  if ( hasWidthExpression )
3884  {
3885  context.setOriginalValueVariable( mWidth );
3887  }
3888  double opacity = mOpacity;
3889  if ( hasOpacityExpression )
3890  {
3891  context.setOriginalValueVariable( mOpacity );
3893  }
3894  QString file = mImageFilePath;
3895  if ( hasFileExpression )
3896  {
3897  context.setOriginalValueVariable( mImageFilePath );
3899  }
3900  applyPattern( mBrush, file, width, opacity, context );
3901 }
3902 
3903 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolRenderContext &context )
3904 {
3905  QSize size;
3906  if ( width > 0 )
3907  {
3908  size.setWidth( context.renderContext().convertToPainterUnits( width, mWidthUnit, mWidthMapUnitScale ) );
3909  size.setHeight( 0 );
3910  }
3911 
3912  bool cached;
3913  QImage img = QgsApplication::imageCache()->pathAsImage( imageFilePath, size, true, alpha, cached, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
3914  if ( img.isNull() )
3915  return;
3916 
3917  brush.setTextureImage( img );
3918 }
3919 
3920 
3921 //
3922 // QgsRandomMarkerFillSymbolLayer
3923 //
3924 
3925 QgsRandomMarkerFillSymbolLayer::QgsRandomMarkerFillSymbolLayer( int pointCount, CountMethod method, double densityArea, unsigned long seed )
3926  : mCountMethod( method )
3927  , mPointCount( pointCount )
3928  , mDensityArea( densityArea )
3929  , mSeed( seed )
3930 {
3931  setSubSymbol( new QgsMarkerSymbol() );
3932 }
3933 
3935 {
3936  const CountMethod countMethod = static_cast< CountMethod >( properties.value( QStringLiteral( "count_method" ), QStringLiteral( "0" ) ).toInt() );
3937  const int pointCount = properties.value( QStringLiteral( "point_count" ), QStringLiteral( "10" ) ).toInt();
3938  const double densityArea = properties.value( QStringLiteral( "density_area" ), QStringLiteral( "250.0" ) ).toDouble();
3939 
3940  unsigned long seed = 0;
3941  if ( properties.contains( QStringLiteral( "seed" ) ) )
3942  seed = properties.value( QStringLiteral( "seed" ) ).toULong();
3943  else
3944  {
3945  // if we a creating a new random marker fill from scratch, we default to a random seed
3946  // because seed based fills are just nicer for users vs seeing points jump around with every map refresh
3947  std::random_device rd;
3948  std::mt19937 mt( seed == 0 ? rd() : seed );
3949  std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3950  seed = uniformDist( mt );
3951  }
3952 
3953  std::unique_ptr< QgsRandomMarkerFillSymbolLayer > sl = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( pointCount, countMethod, densityArea, seed );
3954 
3955  if ( properties.contains( QStringLiteral( "density_area_unit" ) ) )
3956  sl->setDensityAreaUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "density_area_unit" )] ) );
3957  if ( properties.contains( QStringLiteral( "density_area_unit_scale" ) ) )
3958  sl->setDensityAreaUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "density_area_unit_scale" )] ) );
3959 
3960  if ( properties.contains( QStringLiteral( "clip_points" ) ) )
3961  {
3962  sl->setClipPoints( properties[QStringLiteral( "clip_points" )].toInt() );
3963  }
3964 
3965  return sl.release();
3966 }
3967 
3969 {
3970  return QStringLiteral( "RandomMarkerFill" );
3971 }
3972 
3974 {
3975  mMarker->setColor( color );
3976  mColor = color;
3977 }
3978 
3980 {
3981  return mMarker ? mMarker->color() : mColor;
3982 }
3983 
3985 {
3986  mMarker->setOpacity( context.opacity() );
3987  mMarker->startRender( context.renderContext(), context.fields() );
3988 }
3989 
3991 {
3992  mMarker->stopRender( context.renderContext() );
3993 }
3994 
3995 void QgsRandomMarkerFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3996 {
3997  Part part;
3998  part.exterior = points;
3999  if ( rings )
4000  part.rings = *rings;
4001 
4002  if ( mRenderingFeature )
4003  {
4004  // in the middle of rendering a possibly multi-part feature, so we collect all the parts and defer the actual rendering
4005  // until after we've received the final part
4006  mCurrentParts << part;
4007  }
4008  else
4009  {
4010  // not rendering a feature, so we can just render the polygon immediately
4011  render( context.renderContext(), QVector< Part>() << part, context.feature() ? *context.feature() : QgsFeature(), context.selected() );
4012  }
4013 }
4014 
4015 void QgsRandomMarkerFillSymbolLayer::render( QgsRenderContext &context, const QVector<QgsRandomMarkerFillSymbolLayer::Part> &parts, const QgsFeature &feature, bool selected )
4016 {
4017  bool clipPoints = mClipPoints;
4019  {
4020  context.expressionContext().setOriginalValueVariable( clipPoints );
4022  }
4023 
4024  QVector< QgsGeometry > geometryParts;
4025  geometryParts.reserve( parts.size() );
4026  QPainterPath path;
4027 
4028  for ( const Part &part : parts )
4029  {
4030  QgsGeometry geom = QgsGeometry::fromQPolygonF( part.exterior );
4031  if ( !geom.isNull() && !part.rings.empty() )
4032  {
4033  QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.get() );
4034  for ( const QPolygonF &ring : part.rings )
4035  {
4037  }
4038  }
4039  if ( !geom.isGeosValid() )
4040  {
4041  geom = geom.buffer( 0, 0 );
4042  }
4043  geometryParts << geom;
4044 
4045  if ( clipPoints )
4046  {
4047  path.addPolygon( part.exterior );
4048  for ( const QPolygonF &ring : part.rings )
4049  {
4050  path.addPolygon( ring );
4051  }
4052  }
4053  }
4054 
4055  const QgsGeometry geom = geometryParts.count() != 1 ? QgsGeometry::unaryUnion( geometryParts ) : geometryParts.at( 0 );
4056 
4057  if ( clipPoints )
4058  {
4059  context.painter()->save();
4060  context.painter()->setClipPath( path );
4061  }
4062 
4063 
4064  int count = mPointCount;
4066  {
4067  context.expressionContext().setOriginalValueVariable( count );
4069  }
4070 
4071  switch ( mCountMethod )
4072  {
4073  case DensityBasedCount:
4074  {
4075  double densityArea = mDensityArea;
4077  {
4078  context.expressionContext().setOriginalValueVariable( densityArea );
4080  }
4081  densityArea = context.convertToPainterUnits( std::sqrt( densityArea ), mDensityAreaUnit, mDensityAreaUnitScale );
4082  densityArea = std::pow( densityArea, 2 );
4083  count = std::max( 0.0, std::ceil( count * ( geom.area() / densityArea ) ) );
4084  break;
4085  }
4086  case AbsoluteCount:
4087  break;
4088  }
4089 
4090  unsigned long seed = mSeed;
4092  {
4093  context.expressionContext().setOriginalValueVariable( static_cast< unsigned long long >( seed ) );
4095  }
4096 
4097  QVector< QgsPointXY > randomPoints = geom.randomPointsInPolygon( count, seed );
4098 #if 0
4099  // in some cases rendering from top to bottom is nice (e.g. randomised tree markers), but in other cases it's not wanted..
4100  // TODO consider exposing this as an option
4101  std::sort( randomPoints.begin(), randomPoints.end(), []( const QgsPointXY & a, const QgsPointXY & b )->bool
4102  {
4103  return a.y() < b.y();
4104  } );
4105 #endif
4106  for ( const QgsPointXY &p : qgis::as_const( randomPoints ) )
4107  {
4108  mMarker->renderPoint( QPointF( p.x(), p.y() ), feature.isValid() ? &feature : nullptr, context, -1, selected );
4109  }
4110 
4111  if ( clipPoints )
4112  {
4113  context.painter()->restore();
4114  }
4115 }
4116 
4118 {
4119  QgsStringMap map;
4120  map.insert( QStringLiteral( "count_method" ), QString::number( static_cast< int >( mCountMethod ) ) );
4121  map.insert( QStringLiteral( "point_count" ), QString::number( mPointCount ) );
4122  map.insert( QStringLiteral( "density_area" ), QString::number( mDensityArea ) );
4123  map.insert( QStringLiteral( "density_area_unit" ), QgsUnitTypes::encodeUnit( mDensityAreaUnit ) );
4124  map.insert( QStringLiteral( "density_area_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDensityAreaUnitScale ) );
4125  map.insert( QStringLiteral( "seed" ), QString::number( mSeed ) );
4126  map.insert( QStringLiteral( "clip_points" ), QString::number( mClipPoints ) );
4127  return map;
4128 }
4129 
4131 {
4132  std::unique_ptr< QgsRandomMarkerFillSymbolLayer > res = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( mPointCount, mCountMethod, mDensityArea, mSeed );
4133  res->mAngle = mAngle;
4134  res->mColor = mColor;
4135  res->setDensityAreaUnit( mDensityAreaUnit );
4136  res->setDensityAreaUnitScale( mDensityAreaUnitScale );
4137  res->mClipPoints = mClipPoints;
4138  res->setSubSymbol( mMarker->clone() );
4139  copyDataDefinedProperties( res.get() );
4140  copyPaintEffect( res.get() );
4141  return res.release();
4142 }
4143 
4145 {
4146  return mMarker.get();
4147 }
4148 
4150 {
4151  if ( !symbol || symbol->type() != QgsSymbol::Marker )
4152  {
4153  delete symbol;
4154  return false;
4155  }
4156 
4157  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
4158  mColor = mMarker->color();
4159  return true;
4160 }
4161 
4163 {
4164  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
4165 
4166  if ( mMarker )
4167  attributes.unite( mMarker->usedAttributes( context ) );
4168 
4169  return attributes;
4170 }
4171 
4173 {
4175  return true;
4176  if ( mMarker && mMarker->hasDataDefinedProperties() )
4177  return true;
4178  return false;
4179 }
4180 
4182 {
4183  return mPointCount;
4184 }
4185 
4187 {
4188  mPointCount = pointCount;
4189 }
4190 
4192 {
4193  return mSeed;
4194 }
4195 
4197 {
4198  mSeed = seed;
4199 }
4200 
4202 {
4203  return mClipPoints;
4204 }
4205 
4207 {
4208  mClipPoints = clipPoints;
4209 }
4210 
4212 {
4213  return mCountMethod;
4214 }
4215 
4217 {
4218  mCountMethod = method;
4219 }
4220 
4222 {
4223  return mDensityArea;
4224 }
4225 
4227 {
4228  mDensityArea = area;
4229 }
4230 
4232 {
4233  mRenderingFeature = true;
4234  mCurrentParts.clear();
4235 }
4236 
4238 {
4239  mRenderingFeature = false;
4240  render( context, mCurrentParts, feature, false );
4241 }
4242 
4243 
4245 {
4246  if ( mMarker )
4247  {
4248  mMarker->setOutputUnit( unit );
4249  }
4250 }
4251 
4253 {
4254  if ( mMarker )
4255  {
4256  return mMarker->outputUnit();
4257  }
4258  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
4259 }
4260 
4262 {
4263  if ( mMarker )
4264  {
4265  mMarker->setMapUnitScale( scale );
4266  }
4267 }
4268 
4270 {
4271  if ( mMarker )
4272  {
4273  return mMarker->mapUnitScale();
4274  }
4275  return QgsMapUnitScale();
4276 }
4277 
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
QgsMapUnitScale mapUnitScale() const override
QColor color2() const
Returns the color used for the endpoint of the shapeburst fill.
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
#define DEFAULT_SIMPLEFILL_BORDERCOLOR
QgsUnitTypes::RenderUnit mStrokeWidthUnit
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
QgsMapUnitScale mapUnitScale() const override
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
static QgsSymbolLayer * createFromSld(QDomElement &element)
QgsFeatureId id
Definition: qgsfeature.h:64
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QgsSimpleFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, Qt::BrushStyle style=DEFAULT_SIMPLEFILL_STYLE, const QColor &strokeColor=DEFAULT_SIMPLEFILL_BORDERCOLOR, Qt::PenStyle strokeStyle=DEFAULT_SIMPLEFILL_BORDERSTYLE, double strokeWidth=DEFAULT_SIMPLEFILL_BORDERWIDTH, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEFILL_JOINSTYLE)
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
static void multiplyOpacity(QImage &image, double factor)
Multiplies opacity of image pixel values by a factor.
Gradient reference point 1 is centroid.
QgsSimpleFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
double mStrokeWidth
Stroke width.
double rendererScale() const
Returns the renderer map scale.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mDisplacementXUnit
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:221
void setPointCount(int count)
Sets the count of random points to render in the fill.
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used for the gradient fill.
QString svgFilePath() const
Returns the path to the SVG file used to render the fill.
QColor strokeColor() const override
Gets stroke color.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static Qt::BrushStyle decodeBrushStyle(const QString &str)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsSVGFillSymbolLayer(const QString &svgFilePath, double width=20, double rotation=0.0)
Constructor for QgsSVGFillSymbolLayer, using the SVG picture at the specified absolute file path...
#define DEFAULT_SIMPLEFILL_JOINSTYLE
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsCentroidFillSymbolLayer using the specified properties map containing symbol propert...
Gradient reference point 1 x.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:809
QgsMapUnitScale mStrokeWidthMapUnitScale
QColor color() const override
The fill color.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsRasterFillSymbolLayer from a properties map.
QgsRasterFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void setSvgFillColor(const QColor &c)
Sets the fill color used for rendering the SVG content.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:62
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void setColor(const QColor &c) override
The fill color.
QString imageFilePath() const
The path to the raster image used for the fill.
QPointF offset() const
Returns the offset for the shapeburst fill.
Use antialiasing while drawing.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QgsMapUnitScale mapUnitScale() const override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
GradientCoordinateMode coordinateMode() const
Coordinate mode for gradient. Controls how the gradient stops are positioned.
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QgsPointPatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Qt::PenJoinStyle penJoinStyle() const
GradientSpread gradientSpread() const
Gradient spread mode. Controls how the gradient behaves outside of the predefined stops...
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsRandomMarkerFillSymbolLayer using the specified properties map containing symbol pro...
A symbol fill consisting of repeated parallel lines.
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.
QgsUnitTypes::RenderUnit mOffsetUnit
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
double angle() const
QgsMapUnitScale mapUnitScale() const override
Base class for polygon renderers generating texture images.
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used to draw the shapeburst fill.
double y
Definition: qgspointxy.h:48
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
A class to represent a 2D point.
Definition: qgspointxy.h:43
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
QgsUnitTypes::RenderUnit patternWidthUnit() const
Returns the units for the width of the SVG images in the pattern.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
static QString ogrFeatureStyleBrush(const QColor &fillColr)
Create ogr feature style string for brush.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void setRendererScale(double scale)
Sets the renderer map scale.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
virtual QColor strokeColor() const
Gets stroke color.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
QString layerType() const override
Returns a string that represents this layer type.
Whether markers should be clipped to polygon boundaries.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsLinePatternFillSymbolLayer from a properties map.
QString layerType() const override
Returns a string that represents this layer type.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
ShapeburstColorType colorType() const
Returns the color mode used for the shapeburst fill.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QgsMapUnitScale mapUnitScale() const override
static QPointF polygonPointOnSurface(const QPolygonF &points, QList< QPolygonF > *rings=nullptr)
Calculate a point on the surface of a QPolygonF.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
Mixed or unknown units.
Definition: qgsunittypes.h:153
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
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...
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSVGFillSymbolLayer from a properties map.
void _renderPolygon(QPainter *p, const QPolygonF &points, const QList< QPolygonF > *rings, QgsSymbolRenderContext &context)
Default method to render polygon.
bool useWholeShape() const
Returns whether the shapeburst fill is set to cover the entire shape.
Flags flags() const
Returns combination of flags used for rendering.
static QgsSymbolLayer * createFromSld(QDomElement &element)
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:182
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
#define DEFAULT_SIMPLEFILL_COLOR
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Line symbol.
Definition: qgssymbol.h:87
A fill symbol layer which places markers at random locations within polygons.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
QColor color2() const
Color for endpoint of gradient, only used if the gradient color type is set to SimpleTwoColor.
QgsGradientFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsUnitTypes::RenderUnit intervalUnit() const
Returns the units for the interval between symbols.
GradientColorType mGradientColorType
double interval() const
Returns the interval between individual symbols.
const QgsMapUnitScale & svgStrokeWidthMapUnitScale() const
Returns the map unit scale for the pattern&#39;s stroke.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
QColor color() const override
The fill color.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QString layerType() const override
Returns a string that represents this layer type.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QMap< QString, QString > QgsStringMap
Definition: qgis.h:694
QgsMapUnitScale mapUnitScale() const override
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
void setInterval(double interval)
Sets the interval between individual symbols.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSVGFillSymbolLayer from a SLD element.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:895
QColor color() const override
The fill color.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1095
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:431
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Gradient reference point 2 y.
double lineAngle() const
Returns the angle for the parallel lines used to fill the symbol.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEFILL_STYLE
static QString encodeColor(const QColor &color)
void setCoordinateMode(FillCoordinateMode mode)
Set the coordinate mode for fill.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
double offsetY() const
Returns the vertical offset values for points in the pattern.
void setOutputUnit(QgsUnitTypes::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:279
void setOffset(QPointF offset)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
void setMapUnitScale(const QgsMapUnitScale &scale) override
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol at the specified point, using the given render context.
Definition: qgssymbol.cpp:1705
QString layerType() const override
Returns a string that represents this layer type.
QgsUnitTypes::RenderUnit mOffsetUnit
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1251
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
double offset() const
Returns the offset distance for lines within the fill, which is the distance to offset the parallel l...
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
void addInteriorRing(QgsCurve *ring) override
Adds an interior ring to the geometry (takes ownership)
Definition: qgspolygon.cpp:148
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
QgsLinePatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
CountMethod countMethod() const
Returns the count method used to randomly fill the polygon.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
std::unique_ptr< QgsLineSymbol > mStroke
Custom stroke.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
A class for filling symbols with a repeated raster image.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
#define DEFAULT_SIMPLEFILL_BORDERWIDTH
QColor dxfBrushColor(QgsSymbolRenderContext &context) const override
Gets brush/fill color.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
bool isGeosValid(QgsGeometry::ValidityFlags flags=nullptr) const
Checks validity of the geometry using GEOS.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setCountMethod(CountMethod method)
Sets the count method used to randomly fill the polygon.
double offsetX() const
Returns the horizontal offset values for points in the pattern.
static const bool SELECT_FILL_STYLE
Whether fill styles for selected features uses symbol layer style.
void setColor(const QColor &color) override
The fill color.
bool clipPoints() const
Returns true if point markers should be clipped to the polygon boundary.
Geometry collection.
void setWidth(double width)
Sets the width for the whole line symbol.
Definition: qgssymbol.cpp:1789
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:746
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setDensityArea(double area)
Sets the density area used to count the number of points to randomly fill the polygon.
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
Definition: qgssymbol.cpp:1816
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
double densityArea() const
Returns the density area used to count the number of points to randomly fill the polygon.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsGradientFillSymbolLayer using the specified properties map containing symbol propert...
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsPointPatternFillSymbolLayer using the specified properties map containing symbol pro...
QByteArray getImageData(const QString &path, bool blocking=false) const
Gets the SVG content corresponding to the given path.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
virtual QColor color() const
The fill color.
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Filename, eg for svg files.
QColor selectionColor() const
Returns the color to use when rendering selected features.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
QgsUnitTypes::RenderUnit mOffsetYUnit
QgsGradientFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, GradientColorType gradientColorType=SimpleTwoColor, GradientType gradientType=Linear, GradientCoordinateMode coordinateMode=Feature, GradientSpread gradientSpread=Pad)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:52
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void setColor(const QColor &color) override
The fill color.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
CountMethod
Methods to define the number of points randomly filling the polygon.
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:491
Shapeburst fill from edge distance.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsSymbol::SymbolType type() const
bool ignoreRings() const
Returns whether the shapeburst fill is set to ignore polygon interior rings.
virtual double area() const
Returns the planar, 2-dimensional area of the geometry.
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
virtual QString type() const =0
Returns a string representing the color ramp type.
QString layerType() const override
Returns a string that represents this layer type.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=nullptr, Qt::PenCapStyle *penCapStyle=nullptr, QVector< qreal > *customDashPattern=nullptr, double *dashOffset=nullptr)
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double mapUnitsPerPixel() const
Returns current map units per pixel.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QColor fillColor() const override
Gets fill color.
QgsUnitTypes::RenderUnit svgStrokeWidthUnit() const
Returns the units for the stroke width.
QColor svgStrokeColor() const
Returns the stroke color used for rendering the SVG content.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:703
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QColor color() const override
The fill color.
Tiling is based on feature bounding box.
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:362
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
static Qt::PenStyle decodePenStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
void setSeed(unsigned long seed)
Sets the random number seed to use when generating points, or 0 if a truly random sequence will be us...
int pointCount() const
Returns the count of random points to render in the fill.
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
virtual double estimateMaxBleed(const QgsRenderContext &context) const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style...
Definition: qgssymbol.h:759
static bool fillFromSld(QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color)
double lineWidth() const
Returns the width of the line subsymbol used to render the parallel lines in the fill.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsShapeburstFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Gradient reference point 1 y.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QgsMapUnitScale mapUnitScale() const override
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
QgsMapUnitScale mapUnitScale() const override
double dxfAngle(QgsSymbolRenderContext &context) const override
Gets angle.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QgsExpressionContext & expressionContext()
Gets the expression context.
QString layerType() const override
Returns a string that represents this layer type.
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
QString layerType() const override
Returns a string that represents this layer type.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
int geometryPartNum() const
Part number of current geometry.
Definition: qgssymbol.h:827
Line angle, or angle of hash lines for hash line symbols.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
GradientCoordinateMode mCoordinateMode
GradientType gradientType() const
Type of gradient, e.g., linear or radial.
QgsRasterFillSymbolLayer(const QString &imageFilePath=QString())
Constructor for QgsRasterFillSymbolLayer, using a raster fill from the specified imageFilePath.
void setLineWidth(double w)
Sets the width of the line subsymbol used to render the parallel lines in the fill.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsColorRamp from a map of properties.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Marker symbol.
Definition: qgssymbol.h:86
A class for filling symbols with a repeated SVG file.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Stroke style (eg solid, dashed)
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:718
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
const QgsMapUnitScale & intervalMapUnitScale() const
Returns the map unit scale for the interval between symbols.
QString layerType() const override
Returns a string that represents this layer type.
QgsCentroidFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
Qt::BrushStyle dxfBrushStyle() const override
Gets brush/fill style.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsShapeburstFillSymbolLayer using the specified properties map containing symbol prope...
Line symbol layer type which draws repeating marker symbols along a line feature. ...
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:732
QColor svgFillColor() const
Returns the fill color used for rendering the SVG content.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleFillSymbolLayer using the specified properties map containing symbol propertie...
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:121
~QgsShapeburstFillSymbolLayer() override
Struct for storing maximum and minimum scales for measurements in map units.
static QString encodeBrushStyle(Qt::BrushStyle style)
The point count is part of a marker density count.
const QgsMapUnitScale & patternWidthMapUnitScale() const
Returns the map unit scale for the pattern&#39;s width.
static void stackBlur(QImage &image, int radius, bool alphaOnly=false)
Performs a stack blur on an image.
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setSvgFilePath(const QString &svgPath)
Sets the path to the SVG file to render in the fill.
virtual QgsStringMap properties() const =0
Returns a string map containing all the color ramp&#39;s properties.
QString ogrFeatureStyleWidth(double widthScaleFactor) const
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QgsRandomMarkerFillSymbolLayer(int pointCount=10, CountMethod method=AbsoluteCount, double densityArea=250.0, unsigned long seed=0)
Constructor for QgsRandomMarkerFillSymbolLayer, with the specified pointCount.
Qt::PenStyle strokeStyle() const
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:784
#define DEFAULT_SIMPLEFILL_BORDERSTYLE
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
GradientColorType gradientColorType() const
Gradient color mode, controls how gradient color stops are created.
Distance between lines, or length of lines for hash line symbols.
Qt::PenJoinStyle mPenJoinStyle
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context&#39;s map to pixel transform, which transforms between map coordinates and device coordi...
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
double patternWidth() const
Returns the width of the rendered SVG content within the fill (i.e.
unsigned long seed() const
Returns the random number seed to use when generating points, or 0 if a truly random sequence will be...
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsLinePatternFillSymbolLayer from a SLD element.
Secondary color (eg for gradient fills)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double maxDistance() const
Returns the maximum distance from the shape&#39;s boundary which is shaded.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
double opacity() const
Returns the opacity for the raster image used in the fill.
The point count is used as an absolute count of markers.
QgsUnitTypes::RenderUnit mOffsetXUnit
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QgsSymbolLayer * createLineLayerFromSld(QDomElement &element)
QgsRandomMarkerFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static const bool SELECT_FILL_BORDER
Whether fill styles for selected features also highlight symbol stroke.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
QgsGeometry geometry
Definition: qgsfeature.h:67
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
void setColor(const QColor &c) override
The fill color.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
double svgStrokeWidth() const
Returns the stroke width used for rendering the SVG content.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false)
Gets SVG as QPicture&.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
QgsUnitTypes::RenderUnit mDistanceXUnit
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
double area() const
Returns the planar, 2-dimensional area of the geometry.
int blurRadius() const
Returns the blur radius, which controls the amount of blurring applied to the fill.
#define INF
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Shapeburst blur radius.
Polygon geometry type.
Definition: qgspolygon.h:31
QgsUnitTypes::RenderUnit mDisplacementYUnit
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Resolves relative paths into absolute paths and vice versa.
Draw map such that there are no problems between adjacent tiles.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QVector< QgsPointXY > randomPointsInPolygon(int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr) const
Returns a list of count random points generated inside a (multi)polygon geometry. ...
void addStopsToGradient(QGradient *gradient, double opacity=1)
Copy color ramp stops to a QGradient.
double distance() const
Returns the distance between lines in the fill pattern.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Fill style (eg solid, dots)
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false)
Returns the specified path rendered as an image.
virtual QColor fillColor() const
Gets fill color.
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=nullptr)
Create ogr feature style string for pen.
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:456
double width() const
Returns the width used for scaling the image used in the fill.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
QgsSVGFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Gradient reference point 2 is centroid.
FillCoordinateMode
Fill coordinate modes, dictates fill tiling behavior.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsShapeburstFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, ShapeburstColorType colorType=SimpleTwoColor, int blurRadius=0, bool useWholeShape=true, double maxDistance=5)
QgsMapUnitScale mOffsetMapUnitScale
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Gradient reference point 2 x.
QgsPropertyCollection mDataDefinedProperties
QgsUnitTypes::RenderUnit mDistanceYUnit
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
int geometryPartCount() const
Part count of current geometry.
Definition: qgssymbol.h:815
void setClipPoints(bool clipped)
Sets whether point markers should be clipped to the polygon boundary.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1766
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
QgsMapUnitScale mStrokeWidthMapUnitScale
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2009
Horizontal distance between points.
QPointF offset() const
Returns the offset for the fill.
virtual QString layerType() const =0
Returns a string that represents this layer type.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:481
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Vertical distance between points.