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