QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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(), context.renderContext() );
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.renderContext(), 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, QgsRenderContext &context )
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  if ( context.renderingStopped() )
1331  break;
1332 
1333  for ( int y = 0; y < height; y++ )
1334  {
1335  f[y] = im[ x + y * width ];
1336  }
1337  distanceTransform1d( f, height, v, z, d );
1338  for ( int y = 0; y < height; y++ )
1339  {
1340  im[ x + y * width ] = d[y];
1341  }
1342  }
1343 
1344  // transform along rows
1345  for ( int y = 0; y < height; y++ )
1346  {
1347  if ( context.renderingStopped() )
1348  break;
1349 
1350  for ( int x = 0; x < width; x++ )
1351  {
1352  f[x] = im[ x + y * width ];
1353  }
1354  distanceTransform1d( f, width, v, z, d );
1355  for ( int x = 0; x < width; x++ )
1356  {
1357  im[ x + y * width ] = d[x];
1358  }
1359  }
1360 
1361  delete [] d;
1362  delete [] f;
1363  delete [] v;
1364  delete [] z;
1365 }
1366 
1367 /* distance transform of a binary QImage */
1368 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im, QgsRenderContext &context )
1369 {
1370  int width = im->width();
1371  int height = im->height();
1372 
1373  double *dtArray = new double[width * height];
1374 
1375  //load qImage to array
1376  QRgb tmpRgb;
1377  int idx = 0;
1378  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1379  {
1380  if ( context.renderingStopped() )
1381  break;
1382 
1383  const QRgb *scanLine = reinterpret_cast< const QRgb * >( im->constScanLine( heightIndex ) );
1384  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1385  {
1386  tmpRgb = scanLine[widthIndex];
1387  if ( qRed( tmpRgb ) == 0 )
1388  {
1389  //black pixel, so zero distance
1390  dtArray[ idx ] = 0;
1391  }
1392  else
1393  {
1394  //white pixel, so initially set distance as infinite
1395  dtArray[ idx ] = INF;
1396  }
1397  idx++;
1398  }
1399  }
1400 
1401  //calculate squared distance transform
1402  distanceTransform2d( dtArray, width, height, context );
1403 
1404  return dtArray;
1405 }
1406 
1407 void QgsShapeburstFillSymbolLayer::dtArrayToQImage( double *array, QImage *im, QgsColorRamp *ramp, QgsRenderContext &context, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1408 {
1409  int width = im->width();
1410  int height = im->height();
1411 
1412  //find maximum distance value
1413  double maxDistanceValue;
1414 
1415  if ( useWholeShape )
1416  {
1417  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1418  double dtMaxValue = array[0];
1419  for ( int i = 1; i < ( width * height ); ++i )
1420  {
1421  if ( array[i] > dtMaxValue )
1422  {
1423  dtMaxValue = array[i];
1424  }
1425  }
1426 
1427  //values in distance transform are squared
1428  maxDistanceValue = std::sqrt( dtMaxValue );
1429  }
1430  else
1431  {
1432  //use max distance set in symbol properties
1433  maxDistanceValue = maxPixelDistance;
1434  }
1435 
1436  //update the pixels in the provided QImage
1437  int idx = 0;
1438  double squaredVal = 0;
1439  double pixVal = 0;
1440  QColor pixColor;
1441  bool layerHasAlpha = layerAlpha < 1.0;
1442 
1443  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1444  {
1445  if ( context.renderingStopped() )
1446  break;
1447 
1448  QRgb *scanLine = reinterpret_cast< QRgb * >( im->scanLine( heightIndex ) );
1449  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1450  {
1451  //result of distance transform
1452  squaredVal = array[idx];
1453 
1454  //scale result to fit in the range [0, 1]
1455  if ( maxDistanceValue > 0 )
1456  {
1457  pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1458  }
1459  else
1460  {
1461  pixVal = 1.0;
1462  }
1463 
1464  //convert value to color from ramp
1465  pixColor = ramp->color( pixVal );
1466 
1467  int pixAlpha = pixColor.alpha();
1468  if ( ( layerHasAlpha ) || ( pixAlpha != 255 ) )
1469  {
1470  //apply layer's transparency to alpha value
1471  double alpha = pixAlpha * layerAlpha;
1472  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1473  QgsSymbolLayerUtils::premultiplyColor( pixColor, alpha );
1474  }
1475 
1476  scanLine[widthIndex] = pixColor.rgba();
1477  idx++;
1478  }
1479  }
1480 }
1481 
1483 {
1484  QgsStringMap map;
1485  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1486  map[QStringLiteral( "gradient_color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
1487  map[QStringLiteral( "color_type" )] = QString::number( mColorType );
1488  map[QStringLiteral( "blur_radius" )] = QString::number( mBlurRadius );
1489  map[QStringLiteral( "use_whole_shape" )] = QString::number( mUseWholeShape );
1490  map[QStringLiteral( "max_distance" )] = QString::number( mMaxDistance );
1491  map[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
1492  map[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
1493  map[QStringLiteral( "ignore_rings" )] = QString::number( mIgnoreRings );
1494  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1495  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1496  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1497  if ( mGradientRamp )
1498  {
1499  map.unite( mGradientRamp->properties() );
1500  }
1501 
1502  return map;
1503 }
1504 
1506 {
1507  std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >( mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1508  if ( mGradientRamp )
1509  {
1510  sl->setColorRamp( mGradientRamp->clone() );
1511  }
1512  sl->setDistanceUnit( mDistanceUnit );
1513  sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1514  sl->setIgnoreRings( mIgnoreRings );
1515  sl->setOffset( mOffset );
1516  sl->setOffsetUnit( mOffsetUnit );
1517  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1518  copyDataDefinedProperties( sl.get() );
1519  copyPaintEffect( sl.get() );
1520  return sl.release();
1521 }
1522 
1524 {
1525  double offsetBleed = context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1526  return offsetBleed;
1527 }
1528 
1530 {
1531  mDistanceUnit = unit;
1532  mOffsetUnit = unit;
1533 }
1534 
1536 {
1537  if ( mDistanceUnit == mOffsetUnit )
1538  {
1539  return mDistanceUnit;
1540  }
1542 }
1543 
1545 {
1546  mDistanceMapUnitScale = scale;
1547  mOffsetMapUnitScale = scale;
1548 }
1549 
1551 {
1552  if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1553  {
1554  return mDistanceMapUnitScale;
1555  }
1556  return QgsMapUnitScale();
1557 }
1558 
1559 
1560 //QgsImageFillSymbolLayer
1561 
1563 {
1564  setSubSymbol( new QgsLineSymbol() );
1565 }
1566 
1567 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
1568 {
1569  QPainter *p = context.renderContext().painter();
1570  if ( !p )
1571  {
1572  return;
1573  }
1574 
1575  mNextAngle = mAngle;
1576  applyDataDefinedSettings( context );
1577 
1578  p->setPen( QPen( Qt::NoPen ) );
1579 
1580  QTransform bkTransform = mBrush.transform();
1582  {
1583  //transform brush to upper left corner of geometry bbox
1584  QPointF leftCorner = points.boundingRect().topLeft();
1585  QTransform t = mBrush.transform();
1586  t.translate( leftCorner.x(), leftCorner.y() );
1587  mBrush.setTransform( t );
1588  }
1589 
1590  if ( context.selected() )
1591  {
1592  QColor selColor = context.renderContext().selectionColor();
1593  // Alister - this doesn't seem to work here
1594  //if ( ! selectionIsOpaque )
1595  // selColor.setAlphaF( context.alpha() );
1596  p->setBrush( QBrush( selColor ) );
1597  _renderPolygon( p, points, rings, context );
1598  }
1599 
1600  if ( !qgsDoubleNear( mNextAngle, 0.0 ) )
1601  {
1602  QTransform t = mBrush.transform();
1603  t.rotate( mNextAngle );
1604  mBrush.setTransform( t );
1605  }
1606  p->setBrush( mBrush );
1607  _renderPolygon( p, points, rings, context );
1608  if ( mStroke )
1609  {
1610  mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1611  if ( rings )
1612  {
1613  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1614  for ( ; ringIt != rings->constEnd(); ++ringIt )
1615  {
1616  mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
1617  }
1618  }
1619  }
1620 
1621  mBrush.setTransform( bkTransform );
1622 }
1623 
1625 {
1626  if ( !symbol ) //unset current stroke
1627  {
1628  mStroke.reset( nullptr );
1629  return true;
1630  }
1631 
1632  if ( symbol->type() != QgsSymbol::Line )
1633  {
1634  delete symbol;
1635  return false;
1636  }
1637 
1638  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
1639  if ( lineSymbol )
1640  {
1641  mStroke.reset( lineSymbol );
1642  return true;
1643  }
1644 
1645  delete symbol;
1646  return false;
1647 }
1648 
1650 {
1651  mStrokeWidthUnit = unit;
1652 }
1653 
1655 {
1656  return mStrokeWidthUnit;
1657 }
1658 
1660 {
1661  mStrokeWidthMapUnitScale = scale;
1662 }
1663 
1665 {
1666  return mStrokeWidthMapUnitScale;
1667 }
1668 
1670 {
1671  if ( mStroke && mStroke->symbolLayer( 0 ) )
1672  {
1673  double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1674  return subLayerBleed;
1675  }
1676  return 0;
1677 }
1678 
1680 {
1681  double width = mStrokeWidth;
1683  {
1684  context.setOriginalValueVariable( mStrokeWidth );
1686  }
1687  return width * e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1688 }
1689 
1691 {
1692  Q_UNUSED( context )
1693  if ( !mStroke )
1694  {
1695  return QColor( Qt::black );
1696  }
1697  return mStroke->color();
1698 }
1699 
1701 {
1702  return Qt::SolidLine;
1703 #if 0
1704  if ( !mStroke )
1705  {
1706  return Qt::SolidLine;
1707  }
1708  else
1709  {
1710  return mStroke->dxfPenStyle();
1711  }
1712 #endif //0
1713 }
1714 
1715 QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1716 {
1717  QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
1718  if ( mStroke )
1719  attr.unite( mStroke->usedAttributes( context ) );
1720  return attr;
1721 }
1722 
1724 {
1726  return true;
1727  if ( mStroke && mStroke->hasDataDefinedProperties() )
1728  return true;
1729  return false;
1730 }
1731 
1732 
1733 //QgsSVGFillSymbolLayer
1734 
1735 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString &svgFilePath, double width, double angle )
1737  , mPatternWidth( width )
1738 {
1739  mStrokeWidth = 0.3;
1740  mAngle = angle;
1741  mColor = QColor( 255, 255, 255 );
1742  setSvgFilePath( svgFilePath );
1743 }
1744 
1745 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double width, double angle )
1747  , mPatternWidth( width )
1748  , mSvgData( svgData )
1749 {
1750  storeViewBox();
1751  mStrokeWidth = 0.3;
1752  mAngle = angle;
1753  mColor = QColor( 255, 255, 255 );
1754  setSubSymbol( new QgsLineSymbol() );
1755  setDefaultSvgParams();
1756 }
1757 
1759 {
1761  mPatternWidthUnit = unit;
1762  mSvgStrokeWidthUnit = unit;
1763  mStrokeWidthUnit = unit;
1764  mStroke->setOutputUnit( unit );
1765 }
1766 
1768 {
1770  if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit || mStrokeWidthUnit != unit )
1771  {
1773  }
1774  return unit;
1775 }
1776 
1778 {
1780  mPatternWidthMapUnitScale = scale;
1781  mSvgStrokeWidthMapUnitScale = scale;
1782  mStrokeWidthMapUnitScale = scale;
1783 }
1784 
1786 {
1787  if ( QgsImageFillSymbolLayer::mapUnitScale() == mPatternWidthMapUnitScale &&
1788  mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1789  mSvgStrokeWidthMapUnitScale == mStrokeWidthMapUnitScale )
1790  {
1791  return mPatternWidthMapUnitScale;
1792  }
1793  return QgsMapUnitScale();
1794 }
1795 
1796 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString &svgPath )
1797 {
1798  mSvgData = QgsApplication::svgCache()->getImageData( svgPath );
1799  storeViewBox();
1800 
1801  mSvgFilePath = svgPath;
1802  setDefaultSvgParams();
1803 }
1804 
1806 {
1807  QByteArray data;
1808  double width = 20;
1809  QString svgFilePath;
1810  double angle = 0.0;
1811 
1812  if ( properties.contains( QStringLiteral( "width" ) ) )
1813  {
1814  width = properties[QStringLiteral( "width" )].toDouble();
1815  }
1816  if ( properties.contains( QStringLiteral( "svgFile" ) ) )
1817  {
1818  svgFilePath = properties[QStringLiteral( "svgFile" )];
1819  }
1820  if ( properties.contains( QStringLiteral( "angle" ) ) )
1821  {
1822  angle = properties[QStringLiteral( "angle" )].toDouble();
1823  }
1824 
1825  std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1826  if ( !svgFilePath.isEmpty() )
1827  {
1828  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( svgFilePath, width, angle );
1829  }
1830  else
1831  {
1832  if ( properties.contains( QStringLiteral( "data" ) ) )
1833  {
1834  data = QByteArray::fromHex( properties[QStringLiteral( "data" )].toLocal8Bit() );
1835  }
1836  symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( data, width, angle );
1837  }
1838 
1839  //svg parameters
1840  if ( properties.contains( QStringLiteral( "svgFillColor" ) ) )
1841  {
1842  //pre 2.5 projects used "svgFillColor"
1843  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgFillColor" )] ) );
1844  }
1845  else if ( properties.contains( QStringLiteral( "color" ) ) )
1846  {
1847  symbolLayer->setSvgFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
1848  }
1849  if ( properties.contains( QStringLiteral( "svgOutlineColor" ) ) )
1850  {
1851  //pre 2.5 projects used "svgOutlineColor"
1852  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "svgOutlineColor" )] ) );
1853  }
1854  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
1855  {
1856  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
1857  }
1858  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
1859  {
1860  symbolLayer->setSvgStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
1861  }
1862  if ( properties.contains( QStringLiteral( "svgOutlineWidth" ) ) )
1863  {
1864  //pre 2.5 projects used "svgOutlineWidth"
1865  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "svgOutlineWidth" )].toDouble() );
1866  }
1867  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
1868  {
1869  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
1870  }
1871  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
1872  {
1873  symbolLayer->setSvgStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
1874  }
1875 
1876  //units
1877  if ( properties.contains( QStringLiteral( "pattern_width_unit" ) ) )
1878  {
1879  symbolLayer->setPatternWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "pattern_width_unit" )] ) );
1880  }
1881  if ( properties.contains( QStringLiteral( "pattern_width_map_unit_scale" ) ) )
1882  {
1883  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "pattern_width_map_unit_scale" )] ) );
1884  }
1885  if ( properties.contains( QStringLiteral( "svg_outline_width_unit" ) ) )
1886  {
1887  symbolLayer->setSvgStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "svg_outline_width_unit" )] ) );
1888  }
1889  if ( properties.contains( QStringLiteral( "svg_outline_width_map_unit_scale" ) ) )
1890  {
1891  symbolLayer->setSvgStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "svg_outline_width_map_unit_scale" )] ) );
1892  }
1893  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
1894  {
1895  symbolLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
1896  }
1897  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1898  {
1899  symbolLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1900  }
1901 
1902  symbolLayer->restoreOldDataDefinedProperties( properties );
1903 
1904  return symbolLayer.release();
1905 }
1906 
1908 {
1909  QgsStringMap::iterator it = properties.find( QStringLiteral( "svgFile" ) );
1910  if ( it != properties.end() )
1911  {
1912  if ( saving )
1913  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1914  else
1915  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1916  }
1917 }
1918 
1920 {
1921  return QStringLiteral( "SVGFill" );
1922 }
1923 
1924 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush, const QString &svgFilePath, double patternWidth, QgsUnitTypes::RenderUnit patternWidthUnit,
1925  const QColor &svgFillColor, const QColor &svgStrokeColor, double svgStrokeWidth,
1928 {
1929  if ( mSvgViewBox.isNull() )
1930  {
1931  return;
1932  }
1933 
1934  double size = context.renderContext().convertToPainterUnits( patternWidth, patternWidthUnit, patternWidthMapUnitScale );
1935 
1936  if ( static_cast< int >( size ) < 1.0 || 10000.0 < size )
1937  {
1938  brush.setTextureImage( QImage() );
1939  }
1940  else
1941  {
1942  bool fitsInCache = true;
1943  double strokeWidth = context.renderContext().convertToPainterUnits( svgStrokeWidth, svgStrokeWidthUnit, svgStrokeWidthMapUnitScale );
1944  QImage patternImage = QgsApplication::svgCache()->svgAsImage( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1945  context.renderContext().scaleFactor(), fitsInCache );
1946  if ( !fitsInCache )
1947  {
1948  QPicture patternPict = QgsApplication::svgCache()->svgAsPicture( svgFilePath, size, svgFillColor, svgStrokeColor, strokeWidth,
1949  context.renderContext().scaleFactor() );
1950  double hwRatio = 1.0;
1951  if ( patternPict.width() > 0 )
1952  {
1953  hwRatio = static_cast< double >( patternPict.height() ) / static_cast< double >( patternPict.width() );
1954  }
1955  patternImage = QImage( static_cast< int >( size ), static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1956  patternImage.fill( 0 ); // transparent background
1957 
1958  QPainter p( &patternImage );
1959  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1960  }
1961 
1962  QTransform brushTransform;
1963  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
1964  {
1965  QImage transparentImage = patternImage.copy();
1966  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
1967  brush.setTextureImage( transparentImage );
1968  }
1969  else
1970  {
1971  brush.setTextureImage( patternImage );
1972  }
1973  brush.setTransform( brushTransform );
1974  }
1975 }
1976 
1978 {
1979 
1980  applyPattern( mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit, mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
1981 
1982  if ( mStroke )
1983  {
1984  mStroke->startRender( context.renderContext(), context.fields() );
1985  }
1986 }
1987 
1989 {
1990  if ( mStroke )
1991  {
1992  mStroke->stopRender( context.renderContext() );
1993  }
1994 }
1995 
1997 {
1998  QgsStringMap map;
1999  if ( !mSvgFilePath.isEmpty() )
2000  {
2001  map.insert( QStringLiteral( "svgFile" ), mSvgFilePath );
2002  }
2003  else
2004  {
2005  map.insert( QStringLiteral( "data" ), QString( mSvgData.toHex() ) );
2006  }
2007 
2008  map.insert( QStringLiteral( "width" ), QString::number( mPatternWidth ) );
2009  map.insert( QStringLiteral( "angle" ), QString::number( mAngle ) );
2010 
2011  //svg parameters
2012  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2013  map.insert( QStringLiteral( "outline_color" ), QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2014  map.insert( QStringLiteral( "outline_width" ), QString::number( mSvgStrokeWidth ) );
2015 
2016  //units
2017  map.insert( QStringLiteral( "pattern_width_unit" ), QgsUnitTypes::encodeUnit( mPatternWidthUnit ) );
2018  map.insert( QStringLiteral( "pattern_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2019  map.insert( QStringLiteral( "svg_outline_width_unit" ), QgsUnitTypes::encodeUnit( mSvgStrokeWidthUnit ) );
2020  map.insert( QStringLiteral( "svg_outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSvgStrokeWidthMapUnitScale ) );
2021  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2022  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2023  return map;
2024 }
2025 
2027 {
2028  std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2029  if ( !mSvgFilePath.isEmpty() )
2030  {
2031  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth, mAngle );
2032  clonedLayer->setSvgFillColor( mColor );
2033  clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2034  clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2035  }
2036  else
2037  {
2038  clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth, mAngle );
2039  }
2040 
2041  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2042  clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2043  clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2044  clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2045  clonedLayer->setStrokeWidthUnit( mStrokeWidthUnit );
2046  clonedLayer->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2047 
2048  if ( mStroke )
2049  {
2050  clonedLayer->setSubSymbol( mStroke->clone() );
2051  }
2052  copyDataDefinedProperties( clonedLayer.get() );
2053  copyPaintEffect( clonedLayer.get() );
2054  return clonedLayer.release();
2055 }
2056 
2057 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2058 {
2059  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2060  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2061  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2062  element.appendChild( symbolizerElem );
2063 
2064  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2065 
2066  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2067  symbolizerElem.appendChild( fillElem );
2068 
2069  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2070  fillElem.appendChild( graphicFillElem );
2071 
2072  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2073  graphicFillElem.appendChild( graphicElem );
2074 
2075  if ( !mSvgFilePath.isEmpty() )
2076  {
2077  // encode a parametric SVG reference
2078  double patternWidth = QgsSymbolLayerUtils::rescaleUom( mPatternWidth, mPatternWidthUnit, props );
2079  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mSvgStrokeWidth, mSvgStrokeWidthUnit, props );
2080  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mSvgFilePath, mColor, patternWidth, mSvgStrokeColor, strokeWidth );
2081  }
2082  else
2083  {
2084  // TODO: create svg from data
2085  // <se:InlineContent>
2086  symbolizerElem.appendChild( doc.createComment( QStringLiteral( "SVG from data not implemented yet" ) ) );
2087  }
2088 
2089  // <Rotation>
2090  QString angleFunc;
2091  bool ok;
2092  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2093  if ( !ok )
2094  {
2095  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2096  }
2097  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2098  {
2099  angleFunc = QString::number( angle + mAngle );
2100  }
2101  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2102 
2103  if ( mStroke )
2104  {
2105  // the stroke sub symbol should be stored within the Stroke element,
2106  // but it will be stored in a separated LineSymbolizer because it could
2107  // have more than one layer
2108  mStroke->toSld( doc, element, props );
2109  }
2110 }
2111 
2113 {
2114  QString path, mimeType;
2115  QColor fillColor, strokeColor;
2116  Qt::PenStyle penStyle;
2117  double size, strokeWidth;
2118 
2119  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2120  if ( fillElem.isNull() )
2121  return nullptr;
2122 
2123  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2124  if ( graphicFillElem.isNull() )
2125  return nullptr;
2126 
2127  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2128  if ( graphicElem.isNull() )
2129  return nullptr;
2130 
2131  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2132  return nullptr;
2133 
2134  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2135  return nullptr;
2136 
2137  QgsSymbolLayerUtils::lineFromSld( graphicElem, penStyle, strokeColor, strokeWidth );
2138 
2139  QString uom = element.attribute( QStringLiteral( "uom" ) );
2140  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2141  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
2142 
2143  double angle = 0.0;
2144  QString angleFunc;
2145  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2146  {
2147  bool ok;
2148  double d = angleFunc.toDouble( &ok );
2149  if ( ok )
2150  angle = d;
2151  }
2152 
2153  std::unique_ptr< QgsSVGFillSymbolLayer > sl = qgis::make_unique< QgsSVGFillSymbolLayer >( path, size, angle );
2154  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2155  sl->setSvgFillColor( fillColor );
2156  sl->setSvgStrokeColor( strokeColor );
2157  sl->setSvgStrokeWidth( strokeWidth );
2158 
2159  // try to get the stroke
2160  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2161  if ( !strokeElem.isNull() )
2162  {
2164  if ( l )
2165  {
2166  QgsSymbolLayerList layers;
2167  layers.append( l );
2168  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2169  }
2170  }
2171 
2172  return sl.release();
2173 }
2174 
2176 {
2180  {
2181  return; //no data defined settings
2182  }
2183 
2185  {
2186  context.setOriginalValueVariable( mAngle );
2188  }
2189 
2190  double width = mPatternWidth;
2192  {
2193  context.setOriginalValueVariable( mPatternWidth );
2195  }
2196  QString svgFile = mSvgFilePath;
2198  {
2199  context.setOriginalValueVariable( mSvgFilePath );
2201  context.renderContext().pathResolver() );
2202  }
2203  QColor svgFillColor = mColor;
2205  {
2208  }
2209  QColor svgStrokeColor = mSvgStrokeColor;
2211  {
2212  context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mSvgStrokeColor ) );
2214  }
2215  double strokeWidth = mSvgStrokeWidth;
2217  {
2218  context.setOriginalValueVariable( mSvgStrokeWidth );
2220  }
2221  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgStrokeColor, strokeWidth,
2222  mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2223 
2224 }
2225 
2226 void QgsSVGFillSymbolLayer::storeViewBox()
2227 {
2228  if ( !mSvgData.isEmpty() )
2229  {
2230  QSvgRenderer r( mSvgData );
2231  if ( r.isValid() )
2232  {
2233  mSvgViewBox = r.viewBoxF();
2234  return;
2235  }
2236  }
2237 
2238  mSvgViewBox = QRectF();
2239 }
2240 
2241 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2242 {
2243  if ( mSvgFilePath.isEmpty() )
2244  {
2245  return;
2246  }
2247 
2248  bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2249  bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2250  QColor defaultFillColor, defaultStrokeColor;
2251  double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2252  QgsApplication::svgCache()->containsParams( mSvgFilePath, hasFillParam, hasDefaultFillColor, defaultFillColor,
2253  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2254  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2255  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2256  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2257 
2258  double newFillOpacity = hasFillOpacityParam ? mColor.alphaF() : 1.0;
2259  double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2260 
2261  if ( hasDefaultFillColor )
2262  {
2263  mColor = defaultFillColor;
2264  mColor.setAlphaF( newFillOpacity );
2265  }
2266  if ( hasDefaultFillOpacity )
2267  {
2268  mColor.setAlphaF( defaultFillOpacity );
2269  }
2270  if ( hasDefaultStrokeColor )
2271  {
2272  mSvgStrokeColor = defaultStrokeColor;
2273  mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2274  }
2275  if ( hasDefaultStrokeOpacity )
2276  {
2277  mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2278  }
2279  if ( hasDefaultStrokeWidth )
2280  {
2281  mSvgStrokeWidth = defaultStrokeWidth;
2282  }
2283 }
2284 
2285 
2288 {
2289  setSubSymbol( new QgsLineSymbol() );
2290  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
2291 }
2292 
2294 {
2295  mFillLineSymbol->setWidth( w );
2296  mLineWidth = w;
2297 }
2298 
2300 {
2301  mFillLineSymbol->setColor( c );
2302  mColor = c;
2303 }
2304 
2306 {
2307  return mFillLineSymbol ? mFillLineSymbol->color() : mColor;
2308 }
2309 
2311 {
2312  delete mFillLineSymbol;
2313 }
2314 
2316 {
2317  if ( !symbol )
2318  {
2319  return false;
2320  }
2321 
2322  if ( symbol->type() == QgsSymbol::Line )
2323  {
2324  QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
2325  if ( lineSymbol )
2326  {
2327  delete mFillLineSymbol;
2328  mFillLineSymbol = lineSymbol;
2329 
2330  return true;
2331  }
2332  }
2333  delete symbol;
2334  return false;
2335 }
2336 
2338 {
2339  return mFillLineSymbol;
2340 }
2341 
2343 {
2344  QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
2345  if ( mFillLineSymbol )
2346  attr.unite( mFillLineSymbol->usedAttributes( context ) );
2347  return attr;
2348 }
2349 
2351 {
2353  return true;
2354  if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2355  return true;
2356  return false;
2357 }
2358 
2360 {
2361  return 0;
2362 }
2363 
2365 {
2367  mDistanceUnit = unit;
2368  mLineWidthUnit = unit;
2369  mOffsetUnit = unit;
2370 }
2371 
2373 {
2375  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2376  {
2378  }
2379  return unit;
2380 }
2381 
2383 {
2385  mDistanceMapUnitScale = scale;
2386  mLineWidthMapUnitScale = scale;
2387  mOffsetMapUnitScale = scale;
2388 }
2389 
2391 {
2392  if ( QgsImageFillSymbolLayer::mapUnitScale() == mDistanceMapUnitScale &&
2393  mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2394  mLineWidthMapUnitScale == mOffsetMapUnitScale )
2395  {
2396  return mDistanceMapUnitScale;
2397  }
2398  return QgsMapUnitScale();
2399 }
2400 
2402 {
2403  std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2404 
2405  //default values
2406  double lineAngle = 45;
2407  double distance = 5;
2408  double lineWidth = 0.5;
2409  QColor color( Qt::black );
2410  double offset = 0.0;
2411 
2412  if ( properties.contains( QStringLiteral( "lineangle" ) ) )
2413  {
2414  //pre 2.5 projects used "lineangle"
2415  lineAngle = properties[QStringLiteral( "lineangle" )].toDouble();
2416  }
2417  else if ( properties.contains( QStringLiteral( "angle" ) ) )
2418  {
2419  lineAngle = properties[QStringLiteral( "angle" )].toDouble();
2420  }
2421  patternLayer->setLineAngle( lineAngle );
2422 
2423  if ( properties.contains( QStringLiteral( "distance" ) ) )
2424  {
2425  distance = properties[QStringLiteral( "distance" )].toDouble();
2426  }
2427  patternLayer->setDistance( distance );
2428 
2429  if ( properties.contains( QStringLiteral( "linewidth" ) ) )
2430  {
2431  //pre 2.5 projects used "linewidth"
2432  lineWidth = properties[QStringLiteral( "linewidth" )].toDouble();
2433  }
2434  else if ( properties.contains( QStringLiteral( "outline_width" ) ) )
2435  {
2436  lineWidth = properties[QStringLiteral( "outline_width" )].toDouble();
2437  }
2438  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
2439  {
2440  lineWidth = properties[QStringLiteral( "line_width" )].toDouble();
2441  }
2442  patternLayer->setLineWidth( lineWidth );
2443 
2444  if ( properties.contains( QStringLiteral( "color" ) ) )
2445  {
2446  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] );
2447  }
2448  else if ( properties.contains( QStringLiteral( "outline_color" ) ) )
2449  {
2450  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] );
2451  }
2452  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
2453  {
2454  color = QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] );
2455  }
2456  patternLayer->setColor( color );
2457 
2458  if ( properties.contains( QStringLiteral( "offset" ) ) )
2459  {
2460  offset = properties[QStringLiteral( "offset" )].toDouble();
2461  }
2462  patternLayer->setOffset( offset );
2463 
2464 
2465  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
2466  {
2467  patternLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )] ) );
2468  }
2469  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
2470  {
2471  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )] ) );
2472  }
2473  if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
2474  {
2475  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
2476  }
2477  else if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2478  {
2479  patternLayer->setLineWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2480  }
2481  if ( properties.contains( QStringLiteral( "line_width_map_unit_scale" ) ) )
2482  {
2483  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "line_width_map_unit_scale" )] ) );
2484  }
2485  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2486  {
2487  patternLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
2488  }
2489  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2490  {
2491  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
2492  }
2493  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
2494  {
2495  patternLayer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
2496  }
2497  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2498  {
2499  patternLayer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2500  }
2501 
2502  patternLayer->restoreOldDataDefinedProperties( properties );
2503 
2504  return patternLayer.release();
2505 }
2506 
2508 {
2509  return QStringLiteral( "LinePatternFill" );
2510 }
2511 
2512 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double lineAngle, double distance )
2513 {
2514  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2515 
2516  if ( !mFillLineSymbol )
2517  {
2518  return;
2519  }
2520  // We have to make a copy because marker intervals will have to be adjusted
2521  std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2522  if ( !fillLineSymbol )
2523  {
2524  return;
2525  }
2526 
2527  const QgsRenderContext &ctx = context.renderContext();
2528  //double strokePixelWidth = lineWidth * QgsSymbolLayerUtils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2529  double outputPixelDist = ctx.convertToPainterUnits( distance, mDistanceUnit, mDistanceMapUnitScale );
2530  double outputPixelOffset = ctx.convertToPainterUnits( mOffset, mOffsetUnit, mOffsetMapUnitScale );
2531 
2532  // NOTE: this may need to be modified if we ever change from a forced rasterized/brush approach,
2533  // because potentially we may want to allow vector based line pattern fills where the first line
2534  // is offset by a large distance
2535 
2536  // fix truncated pattern with larger offsets
2537  outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2538  if ( outputPixelOffset > outputPixelDist / 2.0 )
2539  outputPixelOffset -= outputPixelDist;
2540 
2541  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2542  // For marker lines we have to get markers interval.
2543  double outputPixelBleed = 0;
2544  double outputPixelInterval = 0; // maximum interval
2545  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2546  {
2547  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2548  double outputPixelLayerBleed = layer->estimateMaxBleed( context.renderContext() );
2549  outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2550 
2551  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2552  if ( markerLineLayer )
2553  {
2554  double outputPixelLayerInterval = ctx.convertToPainterUnits( markerLineLayer->interval(), markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2555 
2556  // There may be multiple marker lines with different intervals.
2557  // In theory we should find the least common multiple, but that could be too
2558  // big (multiplication of intervals in the worst case).
2559  // Because patterns without small common interval would look strange, we
2560  // believe that the longest interval should usually be sufficient.
2561  outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2562  }
2563  }
2564 
2565  if ( outputPixelInterval > 0 )
2566  {
2567  // We have to adjust marker intervals to integer pixel size to get
2568  // repeatable pattern.
2569  double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2570  outputPixelInterval = std::round( outputPixelInterval );
2571 
2572  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2573  {
2574  QgsSymbolLayer *layer = fillLineSymbol->symbolLayer( i );
2575 
2576  QgsMarkerLineSymbolLayer *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayer *>( layer );
2577  if ( markerLineLayer )
2578  {
2579  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2580  }
2581  }
2582  }
2583 
2584  //create image
2585  int height, width;
2586  lineAngle = std::fmod( lineAngle, 360 );
2587  if ( lineAngle < 0 )
2588  lineAngle += 360;
2589  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2590  {
2591  height = outputPixelDist;
2592  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2593  }
2594  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2595  {
2596  width = outputPixelDist;
2597  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2598  }
2599  else
2600  {
2601  height = outputPixelDist / std::cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2602  width = outputPixelDist / std::sin( lineAngle * M_PI / 180 );
2603 
2604  // recalculate real angle and distance after rounding to pixels
2605  lineAngle = 180 * std::atan2( static_cast< double >( height ), static_cast< double >( width ) ) / M_PI;
2606  if ( lineAngle < 0 )
2607  {
2608  lineAngle += 360.;
2609  }
2610 
2611  height = std::abs( height );
2612  width = std::abs( width );
2613 
2614  outputPixelDist = std::abs( height * std::cos( lineAngle * M_PI / 180 ) );
2615 
2616  // Round offset to correspond to one pixel height, otherwise lines may
2617  // be shifted on tile border if offset falls close to pixel center
2618  int offsetHeight = static_cast< int >( std::round( outputPixelOffset / std::cos( lineAngle * M_PI / 180 ) ) );
2619  outputPixelOffset = offsetHeight * std::cos( lineAngle * M_PI / 180 );
2620  }
2621 
2622  //depending on the angle, we might need to render into a larger image and use a subset of it
2623  double dx = 0;
2624  double dy = 0;
2625 
2626  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2627  // thus we add integer multiplications of width and height covering the bleed
2628  int bufferMulti = static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2629 
2630  // Always buffer at least once so that center of line marker in upper right corner
2631  // does not fall outside due to representation error
2632  bufferMulti = std::max( bufferMulti, 1 );
2633 
2634  int xBuffer = width * bufferMulti;
2635  int yBuffer = height * bufferMulti;
2636  int innerWidth = width;
2637  int innerHeight = height;
2638  width += 2 * xBuffer;
2639  height += 2 * yBuffer;
2640 
2641  //protect from zero width/height image and symbol layer from eating too much memory
2642  if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2643  {
2644  return;
2645  }
2646 
2647  QImage patternImage( width, height, QImage::Format_ARGB32 );
2648  patternImage.fill( 0 );
2649 
2650  QPointF p1, p2, p3, p4, p5, p6;
2651  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2652  {
2653  p1 = QPointF( 0, yBuffer );
2654  p2 = QPointF( width, yBuffer );
2655  p3 = QPointF( 0, yBuffer + innerHeight );
2656  p4 = QPointF( width, yBuffer + innerHeight );
2657  }
2658  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2659  {
2660  p1 = QPointF( xBuffer, height );
2661  p2 = QPointF( xBuffer, 0 );
2662  p3 = QPointF( xBuffer + innerWidth, height );
2663  p4 = QPointF( xBuffer + innerWidth, 0 );
2664  }
2665  else if ( lineAngle > 0 && lineAngle < 90 )
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( 0, height );
2670  p2 = QPointF( width, 0 );
2671  p3 = QPointF( -dx, height - dy );
2672  p4 = QPointF( width - dx, -dy );
2673  p5 = QPointF( dx, height + dy );
2674  p6 = QPointF( width + dx, dy );
2675  }
2676  else if ( lineAngle > 180 && lineAngle < 270 )
2677  {
2678  dx = outputPixelDist * std::cos( ( 90 - lineAngle ) * M_PI / 180.0 );
2679  dy = outputPixelDist * std::sin( ( 90 - lineAngle ) * M_PI / 180.0 );
2680  p1 = QPointF( width, 0 );
2681  p2 = QPointF( 0, height );
2682  p3 = QPointF( width - dx, -dy );
2683  p4 = QPointF( -dx, height - dy );
2684  p5 = QPointF( width + dx, dy );
2685  p6 = QPointF( dx, height + dy );
2686  }
2687  else if ( lineAngle > 90 && lineAngle < 180 )
2688  {
2689  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2690  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2691  p1 = QPointF( 0, 0 );
2692  p2 = QPointF( width, height );
2693  p5 = QPointF( dx, -dy );
2694  p6 = QPointF( width + dx, height - dy );
2695  p3 = QPointF( -dx, dy );
2696  p4 = QPointF( width - dx, height + dy );
2697  }
2698  else if ( lineAngle > 270 && lineAngle < 360 )
2699  {
2700  dy = outputPixelDist * std::cos( ( 180 - lineAngle ) * M_PI / 180 );
2701  dx = outputPixelDist * std::sin( ( 180 - lineAngle ) * M_PI / 180 );
2702  p1 = QPointF( width, height );
2703  p2 = QPointF( 0, 0 );
2704  p5 = QPointF( width + dx, height - dy );
2705  p6 = QPointF( dx, -dy );
2706  p3 = QPointF( width - dx, height + dy );
2707  p4 = QPointF( -dx, dy );
2708  }
2709 
2710  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2711  {
2712  QPointF tempPt;
2713  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2714  p3 = QPointF( tempPt.x(), tempPt.y() );
2715  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2716  p4 = QPointF( tempPt.x(), tempPt.y() );
2717  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2718  p5 = QPointF( tempPt.x(), tempPt.y() );
2719  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2720  p6 = QPointF( tempPt.x(), tempPt.y() );
2721 
2722  //update p1, p2 last
2723  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2724  p1 = QPointF( tempPt.x(), tempPt.y() );
2725  tempPt = QgsSymbolLayerUtils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2726  p2 = QPointF( tempPt.x(), tempPt.y() );
2727  }
2728 
2729  QPainter p( &patternImage );
2730 
2731 #if 0
2732  // DEBUG: Draw rectangle
2733  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2734  QPen pen( QColor( Qt::black ) );
2735  pen.setWidthF( 0.1 );
2736  pen.setCapStyle( Qt::FlatCap );
2737  p.setPen( pen );
2738 
2739  // To see this rectangle, comment buffer cut below.
2740  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2741  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2742  p.drawPolygon( polygon );
2743 
2744  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 );
2745  p.drawPolygon( polygon );
2746 #endif
2747 
2748  // Use antialiasing because without antialiasing lines are rendered to the
2749  // right and below the mathematically defined points (not symmetrical)
2750  // and such tiles become useless for are filling
2751  p.setRenderHint( QPainter::Antialiasing, true );
2752 
2753  // line rendering needs context for drawing on patternImage
2754  QgsRenderContext lineRenderContext;
2755  lineRenderContext.setPainter( &p );
2756  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
2758  lineRenderContext.setMapToPixel( mtp );
2759  lineRenderContext.setForceVectorOutput( false );
2760  lineRenderContext.setExpressionContext( context.renderContext().expressionContext() );
2761 
2762  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2763 
2764  QVector<QPolygonF> polygons;
2765  polygons.append( QPolygonF() << p1 << p2 );
2766  polygons.append( QPolygonF() << p3 << p4 );
2767  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2768  {
2769  polygons.append( QPolygonF() << p5 << p6 );
2770  }
2771 
2772  for ( const QPolygonF &polygon : qgis::as_const( polygons ) )
2773  {
2774  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2775  }
2776 
2777  fillLineSymbol->stopRender( lineRenderContext );
2778  p.end();
2779 
2780  // Cut off the buffer
2781  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2782 
2783  //set image to mBrush
2784  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2785  {
2786  QImage transparentImage = patternImage.copy();
2787  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2788  brush.setTextureImage( transparentImage );
2789  }
2790  else
2791  {
2792  brush.setTextureImage( patternImage );
2793  }
2794 
2795  QTransform brushTransform;
2796  brush.setTransform( brushTransform );
2797 }
2798 
2800 {
2801  applyPattern( context, mBrush, mLineAngle, mDistance );
2802 
2803  if ( mFillLineSymbol )
2804  {
2805  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2806  }
2807 }
2808 
2810 {
2811  if ( mFillLineSymbol )
2812  {
2813  mFillLineSymbol->stopRender( context.renderContext() );
2814  }
2815 }
2816 
2818 {
2819  QgsStringMap map;
2820  map.insert( QStringLiteral( "angle" ), QString::number( mLineAngle ) );
2821  map.insert( QStringLiteral( "distance" ), QString::number( mDistance ) );
2822  map.insert( QStringLiteral( "line_width" ), QString::number( mLineWidth ) );
2823  map.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
2824  map.insert( QStringLiteral( "offset" ), QString::number( mOffset ) );
2825  map.insert( QStringLiteral( "distance_unit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
2826  map.insert( QStringLiteral( "line_width_unit" ), QgsUnitTypes::encodeUnit( mLineWidthUnit ) );
2827  map.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
2828  map.insert( QStringLiteral( "distance_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2829  map.insert( QStringLiteral( "line_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2830  map.insert( QStringLiteral( "offset_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2831  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
2832  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
2833  return map;
2834 }
2835 
2837 {
2839  if ( mFillLineSymbol )
2840  {
2841  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2842  }
2843  copyPaintEffect( clonedLayer );
2844  copyDataDefinedProperties( clonedLayer );
2845  return clonedLayer;
2846 }
2847 
2848 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2849 {
2850  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
2851  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
2852  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
2853  element.appendChild( symbolizerElem );
2854 
2855  // <Geometry>
2856  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
2857 
2858  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
2859  symbolizerElem.appendChild( fillElem );
2860 
2861  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
2862  fillElem.appendChild( graphicFillElem );
2863 
2864  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2865  graphicFillElem.appendChild( graphicElem );
2866 
2867  //line properties must be inside the graphic definition
2868  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2869  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2870  lineWidth = QgsSymbolLayerUtils::rescaleUom( lineWidth, mLineWidthUnit, props );
2871  double distance = QgsSymbolLayerUtils::rescaleUom( mDistance, mDistanceUnit, props );
2872  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "horline" ), QColor(), lineColor, Qt::SolidLine, lineWidth, distance );
2873 
2874  // <Rotation>
2875  QString angleFunc;
2876  bool ok;
2877  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2878  if ( !ok )
2879  {
2880  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mLineAngle );
2881  }
2882  else if ( !qgsDoubleNear( angle + mLineAngle, 0.0 ) )
2883  {
2884  angleFunc = QString::number( angle + mLineAngle );
2885  }
2886  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2887 
2888  // <se:Displacement>
2889  QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2890  lineOffset = QgsSymbolLayerUtils::rescaleUom( lineOffset, mOffsetUnit, props );
2891  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, lineOffset );
2892 }
2893 
2894 QString QgsLinePatternFillSymbolLayer::ogrFeatureStyleWidth( double widthScaleFactor ) const
2895 {
2896  QString featureStyle;
2897  featureStyle.append( "Brush(" );
2898  featureStyle.append( QStringLiteral( "fc:%1" ).arg( mColor.name() ) );
2899  featureStyle.append( QStringLiteral( ",bc:%1" ).arg( QStringLiteral( "#00000000" ) ) ); //transparent background
2900  featureStyle.append( ",id:\"ogr-brush-2\"" );
2901  featureStyle.append( QStringLiteral( ",a:%1" ).arg( mLineAngle ) );
2902  featureStyle.append( QStringLiteral( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2903  featureStyle.append( ",dx:0mm" );
2904  featureStyle.append( QStringLiteral( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2905  featureStyle.append( ')' );
2906  return featureStyle;
2907 }
2908 
2910 {
2912  && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
2913  {
2914  return; //no data defined settings
2915  }
2916 
2917  double lineAngle = mLineAngle;
2919  {
2920  context.setOriginalValueVariable( mLineAngle );
2922  }
2923  double distance = mDistance;
2925  {
2926  context.setOriginalValueVariable( mDistance );
2928  }
2929  applyPattern( context, mBrush, lineAngle, distance );
2930 }
2931 
2933 {
2934  QString name;
2935  QColor fillColor, lineColor;
2936  double size, lineWidth;
2937  Qt::PenStyle lineStyle;
2938 
2939  QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
2940  if ( fillElem.isNull() )
2941  return nullptr;
2942 
2943  QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
2944  if ( graphicFillElem.isNull() )
2945  return nullptr;
2946 
2947  QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
2948  if ( graphicElem.isNull() )
2949  return nullptr;
2950 
2951  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2952  return nullptr;
2953 
2954  if ( name != QLatin1String( "horline" ) )
2955  return nullptr;
2956 
2957  double angle = 0.0;
2958  QString angleFunc;
2959  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2960  {
2961  bool ok;
2962  double d = angleFunc.toDouble( &ok );
2963  if ( ok )
2964  angle = d;
2965  }
2966 
2967  double offset = 0.0;
2968  QPointF vectOffset;
2969  if ( QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, vectOffset ) )
2970  {
2971  offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
2972  }
2973 
2974  QString uom = element.attribute( QStringLiteral( "uom" ) );
2975  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2976  lineWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, lineWidth );
2977 
2978  std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2979  sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2980  sl->setColor( lineColor );
2981  sl->setLineWidth( lineWidth );
2982  sl->setLineAngle( angle );
2983  sl->setOffset( offset );
2984  sl->setDistance( size );
2985 
2986  // try to get the stroke
2987  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2988  if ( !strokeElem.isNull() )
2989  {
2991  if ( l )
2992  {
2993  QgsSymbolLayerList layers;
2994  layers.append( l );
2995  sl->setSubSymbol( new QgsLineSymbol( layers ) );
2996  }
2997  }
2998 
2999  return sl.release();
3000 }
3001 
3002 
3004 
3007 {
3008  mDistanceX = 15;
3009  mDistanceY = 15;
3010  mDisplacementX = 0;
3011  mDisplacementY = 0;
3012  mOffsetX = 0;
3013  mOffsetY = 0;
3014  setSubSymbol( new QgsMarkerSymbol() );
3015  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //no stroke
3016 }
3017 
3019 {
3020  delete mMarkerSymbol;
3021 }
3022 
3024 {
3026  mDistanceXUnit = unit;
3027  mDistanceYUnit = unit;
3028  mDisplacementXUnit = unit;
3029  mDisplacementYUnit = unit;
3030  mOffsetXUnit = unit;
3031  mOffsetYUnit = unit;
3032  if ( mMarkerSymbol )
3033  {
3034  mMarkerSymbol->setOutputUnit( unit );
3035  }
3036 
3037 }
3038 
3040 {
3042  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit || mOffsetXUnit != unit || mOffsetYUnit != unit )
3043  {
3045  }
3046  return unit;
3047 }
3048 
3050 {
3052  mDistanceXMapUnitScale = scale;
3053  mDistanceYMapUnitScale = scale;
3056  mOffsetXMapUnitScale = scale;
3057  mOffsetYMapUnitScale = scale;
3058 }
3059 
3061 {
3068  {
3069  return mDistanceXMapUnitScale;
3070  }
3071  return QgsMapUnitScale();
3072 }
3073 
3075 {
3076  std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = qgis::make_unique< QgsPointPatternFillSymbolLayer >();
3077  if ( properties.contains( QStringLiteral( "distance_x" ) ) )
3078  {
3079  layer->setDistanceX( properties[QStringLiteral( "distance_x" )].toDouble() );
3080  }
3081  if ( properties.contains( QStringLiteral( "distance_y" ) ) )
3082  {
3083  layer->setDistanceY( properties[QStringLiteral( "distance_y" )].toDouble() );
3084  }
3085  if ( properties.contains( QStringLiteral( "displacement_x" ) ) )
3086  {
3087  layer->setDisplacementX( properties[QStringLiteral( "displacement_x" )].toDouble() );
3088  }
3089  if ( properties.contains( QStringLiteral( "displacement_y" ) ) )
3090  {
3091  layer->setDisplacementY( properties[QStringLiteral( "displacement_y" )].toDouble() );
3092  }
3093  if ( properties.contains( QStringLiteral( "offset_x" ) ) )
3094  {
3095  layer->setOffsetX( properties[QStringLiteral( "offset_x" )].toDouble() );
3096  }
3097  if ( properties.contains( QStringLiteral( "offset_y" ) ) )
3098  {
3099  layer->setOffsetY( properties[QStringLiteral( "offset_y" )].toDouble() );
3100  }
3101 
3102  if ( properties.contains( QStringLiteral( "distance_x_unit" ) ) )
3103  {
3104  layer->setDistanceXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_x_unit" )] ) );
3105  }
3106  if ( properties.contains( QStringLiteral( "distance_x_map_unit_scale" ) ) )
3107  {
3108  layer->setDistanceXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_x_map_unit_scale" )] ) );
3109  }
3110  if ( properties.contains( QStringLiteral( "distance_y_unit" ) ) )
3111  {
3112  layer->setDistanceYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_y_unit" )] ) );
3113  }
3114  if ( properties.contains( QStringLiteral( "distance_y_map_unit_scale" ) ) )
3115  {
3116  layer->setDistanceYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_y_map_unit_scale" )] ) );
3117  }
3118  if ( properties.contains( QStringLiteral( "displacement_x_unit" ) ) )
3119  {
3120  layer->setDisplacementXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_x_unit" )] ) );
3121  }
3122  if ( properties.contains( QStringLiteral( "displacement_x_map_unit_scale" ) ) )
3123  {
3124  layer->setDisplacementXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_x_map_unit_scale" )] ) );
3125  }
3126  if ( properties.contains( QStringLiteral( "displacement_y_unit" ) ) )
3127  {
3128  layer->setDisplacementYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "displacement_y_unit" )] ) );
3129  }
3130  if ( properties.contains( QStringLiteral( "displacement_y_map_unit_scale" ) ) )
3131  {
3132  layer->setDisplacementYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "displacement_y_map_unit_scale" )] ) );
3133  }
3134  if ( properties.contains( QStringLiteral( "offset_x_unit" ) ) )
3135  {
3136  layer->setOffsetXUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_x_unit" )] ) );
3137  }
3138  if ( properties.contains( QStringLiteral( "offset_x_map_unit_scale" ) ) )
3139  {
3140  layer->setOffsetXMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_x_map_unit_scale" )] ) );
3141  }
3142  if ( properties.contains( QStringLiteral( "offset_y_unit" ) ) )
3143  {
3144  layer->setOffsetYUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_y_unit" )] ) );
3145  }
3146  if ( properties.contains( QStringLiteral( "offset_y_map_unit_scale" ) ) )
3147  {
3148  layer->setOffsetYMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_y_map_unit_scale" )] ) );
3149  }
3150 
3151  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
3152  {
3153  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
3154  }
3155  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3156  {
3157  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3158  }
3159 
3160  layer->restoreOldDataDefinedProperties( properties );
3161 
3162  return layer.release();
3163 }
3164 
3166 {
3167  return QStringLiteral( "PointPatternFill" );
3168 }
3169 
3170 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext &context, QBrush &brush, double distanceX, double distanceY,
3171  double displacementX, double displacementY, double offsetX, double offsetY )
3172 {
3173  //render 3 rows and columns in one go to easily incorporate displacement
3174  const QgsRenderContext &ctx = context.renderContext();
3177 
3178  double widthOffset = std::fmod( ctx.convertToPainterUnits( offsetX, mOffsetXUnit, mOffsetXMapUnitScale ), width );
3179  double heightOffset = std::fmod( ctx.convertToPainterUnits( offsetY, mOffsetYUnit, mOffsetYMapUnitScale ), height );
3180 
3181  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3182  {
3183  QImage img;
3184  brush.setTextureImage( img );
3185  return;
3186  }
3187 
3188  QImage patternImage( width, height, QImage::Format_ARGB32 );
3189  patternImage.fill( 0 );
3190 
3191  if ( mMarkerSymbol )
3192  {
3193  QPainter p( &patternImage );
3194 
3195  //marker rendering needs context for drawing on patternImage
3196  QgsRenderContext pointRenderContext;
3197  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3198  pointRenderContext.setPainter( &p );
3199  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() );
3201  {
3202  pointRenderContext.setFlag( QgsRenderContext::Antialiasing, true );
3203  p.setRenderHint( QPainter::Antialiasing, true );
3204  }
3206  pointRenderContext.setMapToPixel( mtp );
3207  pointRenderContext.setForceVectorOutput( false );
3208  pointRenderContext.setExpressionContext( context.renderContext().expressionContext() );
3209 
3210  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3211 
3212  //render points on distance grid
3213  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3214  {
3215  for ( double currentY = -height; currentY <= height * 2.0; currentY += height )
3216  {
3217  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.feature(), pointRenderContext );
3218  }
3219  }
3220 
3221  //render displaced points
3224  for ( double currentX = -width; currentX <= width * 2.0; currentX += width )
3225  {
3226  for ( double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3227  {
3228  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.feature(), pointRenderContext );
3229  }
3230  }
3231 
3232  for ( double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3233  {
3234  for ( double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3235  {
3236  mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3237  }
3238  }
3239 
3240  mMarkerSymbol->stopRender( pointRenderContext );
3241  }
3242 
3243  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
3244  {
3245  QImage transparentImage = patternImage.copy();
3246  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
3247  brush.setTextureImage( transparentImage );
3248  }
3249  else
3250  {
3251  brush.setTextureImage( patternImage );
3252  }
3253  QTransform brushTransform;
3254  brush.setTransform( brushTransform );
3255 }
3256 
3258 {
3260 
3261  if ( mStroke )
3262  {
3263  mStroke->startRender( context.renderContext(), context.fields() );
3264  }
3265 }
3266 
3268 {
3269  if ( mStroke )
3270  {
3271  mStroke->stopRender( context.renderContext() );
3272  }
3273 }
3274 
3276 {
3277  QgsStringMap map;
3278  map.insert( QStringLiteral( "distance_x" ), QString::number( mDistanceX ) );
3279  map.insert( QStringLiteral( "distance_y" ), QString::number( mDistanceY ) );
3280  map.insert( QStringLiteral( "displacement_x" ), QString::number( mDisplacementX ) );
3281  map.insert( QStringLiteral( "displacement_y" ), QString::number( mDisplacementY ) );
3282  map.insert( QStringLiteral( "offset_x" ), QString::number( mOffsetX ) );
3283  map.insert( QStringLiteral( "offset_y" ), QString::number( mOffsetY ) );
3284  map.insert( QStringLiteral( "distance_x_unit" ), QgsUnitTypes::encodeUnit( mDistanceXUnit ) );
3285  map.insert( QStringLiteral( "distance_y_unit" ), QgsUnitTypes::encodeUnit( mDistanceYUnit ) );
3286  map.insert( QStringLiteral( "displacement_x_unit" ), QgsUnitTypes::encodeUnit( mDisplacementXUnit ) );
3287  map.insert( QStringLiteral( "displacement_y_unit" ), QgsUnitTypes::encodeUnit( mDisplacementYUnit ) );
3288  map.insert( QStringLiteral( "offset_x_unit" ), QgsUnitTypes::encodeUnit( mOffsetXUnit ) );
3289  map.insert( QStringLiteral( "offset_y_unit" ), QgsUnitTypes::encodeUnit( mOffsetYUnit ) );
3290  map.insert( QStringLiteral( "distance_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3291  map.insert( QStringLiteral( "distance_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3292  map.insert( QStringLiteral( "displacement_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3293  map.insert( QStringLiteral( "displacement_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3294  map.insert( QStringLiteral( "offset_x_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetXMapUnitScale ) );
3295  map.insert( QStringLiteral( "offset_y_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetYMapUnitScale ) );
3296  map.insert( QStringLiteral( "outline_width_unit" ), QgsUnitTypes::encodeUnit( mStrokeWidthUnit ) );
3297  map.insert( QStringLiteral( "outline_width_map_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale ) );
3298  return map;
3299 }
3300 
3302 {
3304  if ( mMarkerSymbol )
3305  {
3306  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3307  }
3308  copyDataDefinedProperties( clonedLayer );
3309  copyPaintEffect( clonedLayer );
3310  return clonedLayer;
3311 }
3312 
3313 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3314 {
3315  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3316  {
3317  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PolygonSymbolizer" ) );
3318  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
3319  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
3320  element.appendChild( symbolizerElem );
3321 
3322  // <Geometry>
3323  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
3324 
3325  QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
3326  symbolizerElem.appendChild( fillElem );
3327 
3328  QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
3329  fillElem.appendChild( graphicFillElem );
3330 
3331  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3334  QString dist = QgsSymbolLayerUtils::encodePoint( QPointF( dx, dy ) );
3335  QDomElement distanceElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "distance" ), dist );
3336  symbolizerElem.appendChild( distanceElem );
3337 
3338  QgsSymbolLayer *layer = mMarkerSymbol->symbolLayer( i );
3339  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
3340  if ( !markerLayer )
3341  {
3342  QString errorMsg = QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3343  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3344  }
3345  else
3346  {
3347  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3348  }
3349  }
3350 }
3351 
3353 {
3354  Q_UNUSED( element )
3355  return nullptr;
3356 }
3357 
3359 {
3360  if ( !symbol )
3361  {
3362  return false;
3363  }
3364 
3365  if ( symbol->type() == QgsSymbol::Marker )
3366  {
3367  QgsMarkerSymbol *markerSymbol = static_cast<QgsMarkerSymbol *>( symbol );
3368  delete mMarkerSymbol;
3369  mMarkerSymbol = markerSymbol;
3370  }
3371  return true;
3372 }
3373 
3375 {
3379  {
3380  return;
3381  }
3382 
3383  double distanceX = mDistanceX;
3385  {
3388  }
3389  double distanceY = mDistanceY;
3391  {
3394  }
3395  double displacementX = mDisplacementX;
3397  {
3400  }
3401  double displacementY = mDisplacementY;
3403  {
3406  }
3407  double offsetX = mOffsetX;
3409  {
3412  }
3413  double offsetY = mOffsetY;
3415  {
3418  }
3419  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY, offsetX, offsetY );
3420 }
3421 
3423 {
3424  return 0;
3425 }
3426 
3428 {
3429  QSet<QString> attributes = QgsImageFillSymbolLayer::usedAttributes( context );
3430 
3431  if ( mMarkerSymbol )
3432  attributes.unite( mMarkerSymbol->usedAttributes( context ) );
3433 
3434  return attributes;
3435 }
3436 
3438 {
3440  return true;
3442  return true;
3443  return false;
3444 }
3445 
3447 {
3448  mColor = c;
3449  if ( mMarkerSymbol )
3450  mMarkerSymbol->setColor( c );
3451 }
3452 
3454 {
3455  return mMarkerSymbol ? mMarkerSymbol->color() : mColor;
3456 }
3457 
3459 
3460 
3462 {
3463  setSubSymbol( new QgsMarkerSymbol() );
3464 }
3465 
3467 {
3468  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3469 
3470  if ( properties.contains( QStringLiteral( "point_on_surface" ) ) )
3471  sl->setPointOnSurface( properties[QStringLiteral( "point_on_surface" )].toInt() != 0 );
3472  if ( properties.contains( QStringLiteral( "point_on_all_parts" ) ) )
3473  sl->setPointOnAllParts( properties[QStringLiteral( "point_on_all_parts" )].toInt() != 0 );
3474 
3475  sl->restoreOldDataDefinedProperties( properties );
3476 
3477  return sl.release();
3478 }
3479 
3481 {
3482  return QStringLiteral( "CentroidFill" );
3483 }
3484 
3486 {
3487  mMarker->setColor( color );
3488  mColor = color;
3489 }
3490 
3492 {
3493  return mMarker ? mMarker->color() : mColor;
3494 }
3495 
3497 {
3498  mMarker->setOpacity( context.opacity() );
3499  mMarker->startRender( context.renderContext(), context.fields() );
3500 
3501  mCurrentFeatureId = -1;
3502  mBiggestPartIndex = 0;
3503 }
3504 
3506 {
3507  mMarker->stopRender( context.renderContext() );
3508 }
3509 
3510 void QgsCentroidFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3511 {
3512  Q_UNUSED( rings )
3513 
3514  if ( !mPointOnAllParts )
3515  {
3516  const QgsFeature *feature = context.feature();
3517  if ( feature )
3518  {
3519  if ( feature->id() != mCurrentFeatureId )
3520  {
3521  mCurrentFeatureId = feature->id();
3522  mBiggestPartIndex = 1;
3523 
3524  if ( context.geometryPartCount() > 1 )
3525  {
3526  const QgsGeometry geom = feature->geometry();
3527  const QgsGeometryCollection *geomCollection = static_cast<const QgsGeometryCollection *>( geom.constGet() );
3528 
3529  double area = 0;
3530  double areaBiggest = 0;
3531  for ( int i = 0; i < context.geometryPartCount(); ++i )
3532  {
3533  area = geomCollection->geometryN( i )->area();
3534  if ( area > areaBiggest )
3535  {
3536  areaBiggest = area;
3537  mBiggestPartIndex = i + 1;
3538  }
3539  }
3540  }
3541  }
3542  }
3543  }
3544 
3545  if ( mPointOnAllParts || ( context.geometryPartNum() == mBiggestPartIndex ) )
3546  {
3547  QPointF centroid = mPointOnSurface ? QgsSymbolLayerUtils::polygonPointOnSurface( points ) : QgsSymbolLayerUtils::polygonCentroid( points );
3548  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3549  }
3550 }
3551 
3553 {
3554  QgsStringMap map;
3555  map[QStringLiteral( "point_on_surface" )] = QString::number( mPointOnSurface );
3556  map[QStringLiteral( "point_on_all_parts" )] = QString::number( mPointOnAllParts );
3557  return map;
3558 }
3559 
3561 {
3562  std::unique_ptr< QgsCentroidFillSymbolLayer > x = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3563  x->mAngle = mAngle;
3564  x->mColor = mColor;
3565  x->setSubSymbol( mMarker->clone() );
3566  x->setPointOnSurface( mPointOnSurface );
3567  x->setPointOnAllParts( mPointOnAllParts );
3568  copyDataDefinedProperties( x.get() );
3569  copyPaintEffect( x.get() );
3570  return x.release();
3571 }
3572 
3573 void QgsCentroidFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3574 {
3575  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3576  // used with PointSymbolizer, then the semantic is to use the centroid
3577  // of the geometry, or any similar representative point.
3578  mMarker->toSld( doc, element, props );
3579 }
3580 
3582 {
3584  if ( !l )
3585  return nullptr;
3586 
3587  QgsSymbolLayerList layers;
3588  layers.append( l );
3589  std::unique_ptr< QgsMarkerSymbol > marker( new QgsMarkerSymbol( layers ) );
3590 
3591  std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3592  sl->setSubSymbol( marker.release() );
3593  sl->setPointOnAllParts( false );
3594  return sl.release();
3595 }
3596 
3597 
3599 {
3600  return mMarker.get();
3601 }
3602 
3604 {
3605  if ( !symbol || symbol->type() != QgsSymbol::Marker )
3606  {
3607  delete symbol;
3608  return false;
3609  }
3610 
3611  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
3612  mColor = mMarker->color();
3613  return true;
3614 }
3615 
3617 {
3618  QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
3619 
3620  if ( mMarker )
3621  attributes.unite( mMarker->usedAttributes( context ) );
3622 
3623  return attributes;
3624 }
3625 
3627 {
3629  return true;
3630  if ( mMarker && mMarker->hasDataDefinedProperties() )
3631  return true;
3632  return false;
3633 }
3634 
3636 {
3637  if ( mMarker )
3638  {
3639  mMarker->setOutputUnit( unit );
3640  }
3641 }
3642 
3644 {
3645  if ( mMarker )
3646  {
3647  return mMarker->outputUnit();
3648  }
3649  return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
3650 }
3651 
3653 {
3654  if ( mMarker )
3655  {
3656  mMarker->setMapUnitScale( scale );
3657  }
3658 }
3659 
3661 {
3662  if ( mMarker )
3663  {
3664  return mMarker->mapUnitScale();
3665  }
3666  return QgsMapUnitScale();
3667 }
3668 
3669 
3670 
3671 
3674  , mImageFilePath( imageFilePath )
3675 {
3676  QgsImageFillSymbolLayer::setSubSymbol( nullptr ); //disable sub symbol
3677 }
3678 
3680 {
3682  double alpha = 1.0;
3683  QPointF offset;
3684  double angle = 0.0;
3685  double width = 0.0;
3686 
3687  QString imagePath;
3688  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3689  {
3690  imagePath = properties[QStringLiteral( "imageFile" )];
3691  }
3692  if ( properties.contains( QStringLiteral( "coordinate_mode" ) ) )
3693  {
3694  mode = static_cast< FillCoordinateMode >( properties[QStringLiteral( "coordinate_mode" )].toInt() );
3695  }
3696  if ( properties.contains( QStringLiteral( "alpha" ) ) )
3697  {
3698  alpha = properties[QStringLiteral( "alpha" )].toDouble();
3699  }
3700  if ( properties.contains( QStringLiteral( "offset" ) ) )
3701  {
3702  offset = QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] );
3703  }
3704  if ( properties.contains( QStringLiteral( "angle" ) ) )
3705  {
3706  angle = properties[QStringLiteral( "angle" )].toDouble();
3707  }
3708  if ( properties.contains( QStringLiteral( "width" ) ) )
3709  {
3710  width = properties[QStringLiteral( "width" )].toDouble();
3711  }
3712  std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = qgis::make_unique< QgsRasterFillSymbolLayer >( imagePath );
3713  symbolLayer->setCoordinateMode( mode );
3714  symbolLayer->setOpacity( alpha );
3715  symbolLayer->setOffset( offset );
3716  symbolLayer->setAngle( angle );
3717  symbolLayer->setWidth( width );
3718  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3719  {
3720  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
3721  }
3722  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3723  {
3724  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
3725  }
3726  if ( properties.contains( QStringLiteral( "width_unit" ) ) )
3727  {
3728  symbolLayer->setWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "width_unit" )] ) );
3729  }
3730  if ( properties.contains( QStringLiteral( "width_map_unit_scale" ) ) )
3731  {
3732  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "width_map_unit_scale" )] ) );
3733  }
3734 
3735  symbolLayer->restoreOldDataDefinedProperties( properties );
3736 
3737  return symbolLayer.release();
3738 }
3739 
3741 {
3742  QgsStringMap::iterator it = properties.find( QStringLiteral( "imageFile" ) );
3743  if ( it != properties.end() )
3744  {
3745  if ( saving )
3746  it.value() = pathResolver.writePath( it.value() );
3747  else
3748  it.value() = pathResolver.readPath( it.value() );
3749  }
3750 }
3751 
3753 {
3754  Q_UNUSED( symbol )
3755  return true;
3756 }
3757 
3759 {
3760  return QStringLiteral( "RasterFill" );
3761 }
3762 
3763 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
3764 {
3765  QPainter *p = context.renderContext().painter();
3766  if ( !p )
3767  {
3768  return;
3769  }
3770 
3771  QPointF offset;
3772  if ( !mOffset.isNull() )
3773  {
3774  offset.setX( context.renderContext().convertToPainterUnits( mOffset.x(), mOffsetUnit, mOffsetMapUnitScale ) );
3775  offset.setY( context.renderContext().convertToPainterUnits( mOffset.y(), mOffsetUnit, mOffsetMapUnitScale ) );
3776  p->translate( offset );
3777  }
3778  if ( mCoordinateMode == Feature )
3779  {
3780  QRectF boundingRect = points.boundingRect();
3781  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3782  boundingRect.top() - mBrush.transform().dy() ) );
3783  }
3784 
3785  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3786  if ( !mOffset.isNull() )
3787  {
3788  p->translate( -offset );
3789  }
3790 }
3791 
3793 {
3794  applyPattern( mBrush, mImageFilePath, mWidth, mOpacity, context );
3795 }
3796 
3798 {
3799  Q_UNUSED( context )
3800 }
3801 
3803 {
3804  QgsStringMap map;
3805  map[QStringLiteral( "imageFile" )] = mImageFilePath;
3806  map[QStringLiteral( "coordinate_mode" )] = QString::number( mCoordinateMode );
3807  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3808  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3809  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3810  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3811  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3812  map[QStringLiteral( "width" )] = QString::number( mWidth );
3813  map[QStringLiteral( "width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
3814  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
3815  return map;
3816 }
3817 
3819 {
3820  std::unique_ptr< QgsRasterFillSymbolLayer > sl = qgis::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
3821  sl->setCoordinateMode( mCoordinateMode );
3822  sl->setOpacity( mOpacity );
3823  sl->setOffset( mOffset );
3824  sl->setOffsetUnit( mOffsetUnit );
3825  sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
3826  sl->setAngle( mAngle );
3827  sl->setWidth( mWidth );
3828  sl->setWidthUnit( mWidthUnit );
3829  sl->setWidthMapUnitScale( mWidthMapUnitScale );
3830  copyDataDefinedProperties( sl.get() );
3831  copyPaintEffect( sl.get() );
3832  return sl.release();
3833 }
3834 
3836 {
3837  return context.convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
3838 }
3839 
3840 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
3841 {
3842  mImageFilePath = imagePath;
3843 }
3844 
3846 {
3847  mCoordinateMode = mode;
3848 }
3849 
3851 {
3852  mOpacity = opacity;
3853 }
3854 
3856 {
3857  if ( !dataDefinedProperties().hasActiveProperties() )
3858  return; // shortcut
3859 
3860  bool hasWidthExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
3861  bool hasFileExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFile );
3862  bool hasOpacityExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity );
3863  bool hasAngleExpression = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
3864 
3865  if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
3866  {
3867  return; //no data defined settings
3868  }
3869 
3870  bool ok;
3871  if ( hasAngleExpression )
3872  {
3873  context.setOriginalValueVariable( mAngle );
3875  if ( ok )
3876  mNextAngle = nextAngle;
3877  }
3878 
3879  if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
3880  {
3881  return; //nothing further to do
3882  }
3883 
3884  double width = mWidth;
3885  if ( hasWidthExpression )
3886  {
3887  context.setOriginalValueVariable( mWidth );
3889  }
3890  double opacity = mOpacity;
3891  if ( hasOpacityExpression )
3892  {
3893  context.setOriginalValueVariable( mOpacity );
3895  }
3896  QString file = mImageFilePath;
3897  if ( hasFileExpression )
3898  {
3899  context.setOriginalValueVariable( mImageFilePath );
3901  }
3902  applyPattern( mBrush, file, width, opacity, context );
3903 }
3904 
3905 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolRenderContext &context )
3906 {
3907  QSize size;
3908  if ( width > 0 )
3909  {
3910  size.setWidth( context.renderContext().convertToPainterUnits( width, mWidthUnit, mWidthMapUnitScale ) );
3911  size.setHeight( 0 );
3912  }
3913 
3914  bool cached;
3915  QImage img = QgsApplication::imageCache()->pathAsImage( imageFilePath, size, true, alpha, cached );
3916  if ( img.isNull() )
3917  return;
3918 
3919  brush.setTextureImage( img );
3920 }
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
QgsMapUnitScale mapUnitScale() const override
QColor color2() const
Returns the color used for the endpoint of the shapeburst fill.
#define DEFAULT_SIMPLEFILL_BORDERCOLOR
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
QgsUnitTypes::RenderUnit mStrokeWidthUnit
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
QgsMapUnitScale mapUnitScale() const override
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
static QgsSymbolLayer * createFromSld(QDomElement &element)
QgsFeatureId id
Definition: qgsfeature.h:64
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
void stopRender(QgsSymbolRenderContext &context) override
QgsSimpleFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, Qt::BrushStyle style=DEFAULT_SIMPLEFILL_STYLE, const QColor &strokeColor=DEFAULT_SIMPLEFILL_BORDERCOLOR, Qt::PenStyle strokeStyle=DEFAULT_SIMPLEFILL_BORDERSTYLE, double strokeWidth=DEFAULT_SIMPLEFILL_BORDERWIDTH, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEFILL_JOINSTYLE)
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
Gradient reference point 1 is centroid.
QgsSimpleFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
double mStrokeWidth
Stroke width.
double rendererScale() const
Returns the renderer map scale.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mDisplacementXUnit
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:154
void setColorRamp(QgsColorRamp *ramp)
Sets the color ramp used for the gradient fill.
QString svgFilePath() const
Returns the path to the SVG file used to render the fill.
QColor strokeColor() const override
Gets stroke color.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static Qt::BrushStyle decodeBrushStyle(const QString &str)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
QgsSVGFillSymbolLayer(const QString &svgFilePath, double width=20, double rotation=0.0)
Constructor for QgsSVGFillSymbolLayer, using the SVG picture at the specified absolute file path...
#define DEFAULT_SIMPLEFILL_JOINSTYLE
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Gradient reference point 1 x.
QByteArray getImageData(const QString &path) const
Gets image data.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:680
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.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
ShapeburstColorType colorType() const
Returns the color mode used for the shapeburst fill.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QgsMapUnitScale mapUnitScale() const override
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:111
Mixed or unknown units.
Definition: qgsunittypes.h:119
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.
QgsUnitTypes::RenderUnit intervalUnit() const
Returns the units for the interval between symbols.
GradientColorType mGradientColorType
double interval() const
Returns the interval between individual symbols.
const QgsMapUnitScale & svgStrokeWidthMapUnitScale() const
Returns the map unit scale for the pattern&#39;s stroke.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
QColor color() const override
The fill color.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QString layerType() const override
Returns a string that represents this layer type.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
void setInterval(double interval)
Sets the interval between individual symbols.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSVGFillSymbolLayer from a SLD element.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:766
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:966
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:422
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Gradient reference point 2 y.
double lineAngle() const
Returns the angle for the parallel lines used to fill the symbol.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEFILL_STYLE
static QString encodeColor(const QColor &color)
void setCoordinateMode(FillCoordinateMode mode)
Set the coordinate mode for fill.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
double offsetY() const
Returns the vertical offset values for points in the pattern.
void setOutputUnit(QgsUnitTypes::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:275
void setOffset(QPointF offset)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
static QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol at the specified point, using the given render context.
Definition: qgssymbol.cpp:1580
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:1125
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
double offset() const
Returns the offset distance for lines within the fill, which is the distance to offset the parallel l...
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
QgsLinePatternFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
std::unique_ptr< QgsLineSymbol > mStroke
Custom stroke.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
A class for filling symbols with a repeated raster image.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
#define DEFAULT_SIMPLEFILL_BORDERWIDTH
QColor dxfBrushColor(QgsSymbolRenderContext &context) const override
Gets brush/fill color.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
double offsetX() const
Returns the horizontal offset values for points in the pattern.
static const bool SELECT_FILL_STYLE
Whether fill styles for selected features uses symbol layer style.
void setColor(const QColor &color) override
The fill color.
Geometry collection.
void setWidth(double width)
Sets the width for the whole line symbol.
Definition: qgssymbol.cpp:1664
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:617
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:1691
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
Returns the color to use when rendering selected features.
QgsUnitTypes::RenderUnit mOffsetYUnit
QgsGradientFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, GradientColorType gradientColorType=SimpleTwoColor, GradientType gradientType=Linear, GradientCoordinateMode coordinateMode=Feature, GradientSpread gradientSpread=Pad)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void startRender(QgsSymbolRenderContext &context) override
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:51
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:480
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:574
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:358
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
Returns true if symbols should be rendered using the selected symbol coloring and style...
Definition: qgssymbol.h:630
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:698
Line angle, or angle of hash lines for hash line symbols.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
GradientCoordinateMode mCoordinateMode
GradientType gradientType() const
Type of gradient, e.g., linear or radial.
QgsRasterFillSymbolLayer(const QString &imageFilePath=QString())
Constructor for QgsRasterFillSymbolLayer, using a raster fill from the specified imageFilePath.
void setLineWidth(double w)
Sets the width of the line subsymbol used to render the parallel lines in the fill.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static QPointF polygonCentroid(const QPolygonF &points)
Calculate the centroid point of a QPolygonF.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsColorRamp from a map of properties.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Marker symbol.
Definition: qgssymbol.h:85
A class for filling symbols with a repeated SVG file.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Stroke style (eg solid, dashed)
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:684
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
const QgsMapUnitScale & intervalMapUnitScale() const
Returns the map unit scale for the interval between symbols.
QString layerType() const override
Returns a string that represents this layer type.
QgsCentroidFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
Qt::BrushStyle dxfBrushStyle() const override
Gets brush/fill style.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Line symbol layer type which draws repeating marker symbols along a line feature. ...
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:698
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
Returns the current feature being rendered.
Definition: qgssymbol.h:655
#define DEFAULT_SIMPLEFILL_BORDERSTYLE
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
GradientColorType gradientColorType() const
Gradient color mode, controls how gradient color stops are created.
Distance between lines, or length of lines for hash line symbols.
Qt::PenJoinStyle mPenJoinStyle
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context&#39;s map to pixel transform, which transforms between map coordinates and device coordi...
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
double patternWidth() const
Returns the width of the rendered SVG content within the fill (i.e.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsLinePatternFillSymbolLayer from a SLD element.
Secondary color (eg for gradient fills)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double maxDistance() const
Returns the maximum distance from the shape&#39;s boundary which is shaded.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void applyDataDefinedSettings(QgsSymbolRenderContext &context) override
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
double opacity() const
Returns the opacity for the raster image used in the fill.
QgsUnitTypes::RenderUnit mOffsetXUnit
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QgsSymbolLayer * createLineLayerFromSld(QDomElement &element)
void setMapUnitScale(const QgsMapUnitScale &scale) override
static const bool SELECT_FILL_BORDER
Whether fill styles for selected features also highlight symbol stroke.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void fillToSld(QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color=QColor())
QgsGeometry geometry
Definition: qgsfeature.h:67
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
void setColor(const QColor &c) override
The fill color.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
double svgStrokeWidth() const
Returns the stroke width used for rendering the SVG content.
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:445
double width() const
Returns the width used for scaling the image used in the fill.
QgsSVGFillSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Gradient reference point 2 is centroid.
FillCoordinateMode
Fill coordinate modes, dictates fill tiling behavior.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsShapeburstFillSymbolLayer(const QColor &color=DEFAULT_SIMPLEFILL_COLOR, const QColor &color2=Qt::white, ShapeburstColorType colorType=SimpleTwoColor, int blurRadius=0, bool useWholeShape=true, double maxDistance=5)
QgsMapUnitScale mOffsetMapUnitScale
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Gradient reference point 2 x.
QgsPropertyCollection mDataDefinedProperties
QgsUnitTypes::RenderUnit mDistanceYUnit
void startRender(QgsSymbolRenderContext &context) override
int geometryPartCount() const
Part count of current geometry.
Definition: qgssymbol.h:686
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1641
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:1884
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:470
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Vertical distance between points.