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