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