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