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