QGIS API Documentation  3.27.0-Master (aef1b1ec20)
qgslinesymbol.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinesymbol.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 "qgslinesymbol.h"
17 #include "qgslinesymbollayer.h"
19 #include "qgssymbollayerutils.h"
20 #include "qgspainteffect.h"
21 
22 QgsLineSymbol *QgsLineSymbol::createSimple( const QVariantMap &properties )
23 {
25  if ( !sl )
26  return nullptr;
27 
28  QgsSymbolLayerList layers;
29  layers.append( sl );
30  return new QgsLineSymbol( layers );
31 }
32 
34  : QgsSymbol( Qgis::SymbolType::Line, layers )
35 {
36  if ( mLayers.isEmpty() )
37  mLayers.append( new QgsSimpleLineSymbolLayer() );
38 }
39 
40 void QgsLineSymbol::setWidth( double w ) const
41 {
42  const double origWidth = width();
43 
44  const auto constMLayers = mLayers;
45  for ( QgsSymbolLayer *layer : constMLayers )
46  {
47  QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
48  if ( lineLayer )
49  {
50  if ( qgsDoubleNear( lineLayer->width(), origWidth ) )
51  {
52  lineLayer->setWidth( w );
53  }
54  else if ( !qgsDoubleNear( origWidth, 0.0 ) )
55  {
56  // proportionally scale the width
57  lineLayer->setWidth( lineLayer->width() * w / origWidth );
58  }
59  // also scale offset to maintain relative position
60  if ( !qgsDoubleNear( origWidth, 0.0 ) && !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
61  lineLayer->setOffset( lineLayer->offset() * w / origWidth );
62  }
63  else
64  {
65  QgsGeometryGeneratorSymbolLayer *geomGeneratorLayer = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
66  if ( geomGeneratorLayer && geomGeneratorLayer->symbolType() == Qgis::SymbolType::Line )
67  {
68  QgsLineSymbol *lineSymbol = qgis::down_cast<QgsLineSymbol *>( geomGeneratorLayer->subSymbol() );
69  if ( qgsDoubleNear( lineSymbol->width(), origWidth ) )
70  {
71  lineSymbol->setWidth( w );
72  }
73  else if ( !qgsDoubleNear( origWidth, 0.0 ) )
74  {
75  // proportionally scale the width
76  lineSymbol->setWidth( lineSymbol->width() * w / origWidth );
77  }
78  }
79  }
80  }
81 }
82 
84 {
85  const auto constLLayers = mLayers;
86  for ( QgsSymbolLayer *layer : constLLayers )
87  {
88  if ( layer->type() != Qgis::SymbolType::Line )
89  continue;
90 
91  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( layer );
92  lineLayer->setWidthUnit( unit );
93  }
94 }
95 
96 double QgsLineSymbol::width() const
97 {
98  double maxWidth = 0;
99  if ( mLayers.isEmpty() )
100  return maxWidth;
101 
102  const auto constMLayers = mLayers;
103  for ( QgsSymbolLayer *symbolLayer : constMLayers )
104  {
105  const QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( symbolLayer );
106  if ( lineLayer )
107  {
108  const double width = lineLayer->width();
109  if ( width > maxWidth )
110  maxWidth = width;
111  }
112  else
113  {
114  QgsGeometryGeneratorSymbolLayer *geomGeneratorLayer = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
115  if ( geomGeneratorLayer && geomGeneratorLayer->symbolType() == Qgis::SymbolType::Line )
116  {
117  QgsLineSymbol *lineSymbol = qgis::down_cast<QgsLineSymbol *>( geomGeneratorLayer->subSymbol() );
118  const double width = lineSymbol->width();
119  if ( width > maxWidth )
120  maxWidth = width;
121  }
122  }
123  }
124  return maxWidth;
125 }
126 
127 double QgsLineSymbol::width( const QgsRenderContext &context ) const
128 {
129  // return width of the largest symbol
130  double maxWidth = 0;
131  for ( QgsSymbolLayer *layer : mLayers )
132  {
133  if ( layer->type() != Qgis::SymbolType::Line )
134  continue;
135  const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
136  const double layerWidth = lineLayer->width( context );
137  maxWidth = std::max( maxWidth, layerWidth );
138  }
139  return maxWidth;
140 }
141 
142 void QgsLineSymbol::setDataDefinedWidth( const QgsProperty &property ) const
143 {
144  const double symbolWidth = width();
145 
146  const auto constMLayers = mLayers;
147  for ( QgsSymbolLayer *layer : constMLayers )
148  {
149  QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
150 
151  if ( lineLayer )
152  {
153  if ( !property )
154  {
157  }
158  else
159  {
160  if ( qgsDoubleNear( symbolWidth, 0.0 ) || qgsDoubleNear( lineLayer->width(), symbolWidth ) )
161  {
163  }
164  else
165  {
166  lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->width() / symbolWidth, property ) );
167  }
168 
169  if ( !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
170  {
171  lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->offset() / symbolWidth, property ) );
172  }
173  }
174  }
175  }
176 }
177 
179 {
180  const double symbolWidth = width();
181 
182  QgsProperty symbolDD;
183 
184  // find the base of the "en masse" pattern
185  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
186  {
187  const QgsLineSymbolLayer *layer = dynamic_cast<const QgsLineSymbolLayer *>( *it );
188  if ( layer && qgsDoubleNear( layer->width(), symbolWidth ) && layer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
189  {
190  symbolDD = layer->dataDefinedProperties().property( QgsSymbolLayer::PropertyStrokeWidth );
191  break;
192  }
193  }
194 
195  if ( !symbolDD )
196  return QgsProperty();
197 
198  // check that all layers width expressions match the "en masse" pattern
199  const auto constMLayers = mLayers;
200  for ( QgsSymbolLayer *layer : constMLayers )
201  {
202  if ( layer->type() != Qgis::SymbolType::Line )
203  continue;
204  const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
205 
207  const QgsProperty layerOffsetDD = lineLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyOffset );
208 
209  if ( qgsDoubleNear( lineLayer->width(), symbolWidth ) )
210  {
211  if ( !layerWidthDD || layerWidthDD != symbolDD )
212  return QgsProperty();
213  }
214  else
215  {
216  if ( qgsDoubleNear( symbolWidth, 0.0 ) )
217  return QgsProperty();
218 
219  const QgsProperty scaledDD( QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->width() / symbolWidth, symbolDD ) );
220  if ( !layerWidthDD || layerWidthDD != scaledDD )
221  return QgsProperty();
222  }
223 
224  const QgsProperty scaledOffsetDD( QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->offset() / symbolWidth, symbolDD ) );
225  if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
226  return QgsProperty();
227  }
228 
229  return symbolDD;
230 }
231 
232 void QgsLineSymbol::renderPolyline( const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
233 {
235  : mOpacity;
236 
237  //save old painter
238  QPainter *renderPainter = context.painter();
239  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
241  symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
242  symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
243 
244  if ( layerIdx != -1 )
245  {
246  QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
248  {
250  {
251  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
252  renderPolylineUsingLayer( lineLayer, points, symbolContext );
253  }
254  else
255  renderUsingLayer( symbolLayer, symbolContext, QgsWkbTypes::LineGeometry, &points );
256  }
257  return;
258  }
259 
260  const auto constMLayers = mLayers;
261  for ( QgsSymbolLayer *symbolLayer : constMLayers )
262  {
263  if ( context.renderingStopped() )
264  break;
265 
266  if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
267  continue;
268 
270  {
271  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
272  renderPolylineUsingLayer( lineLayer, points, symbolContext );
273  }
274  else
275  {
276  renderUsingLayer( symbolLayer, symbolContext, QgsWkbTypes::LineGeometry, &points );
277  }
278  }
279 
280  context.setPainter( renderPainter );
281 }
282 
283 void QgsLineSymbol::renderPolylineUsingLayer( QgsLineSymbolLayer *layer, const QPolygonF &points, QgsSymbolRenderContext &context )
284 {
285  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
286  return;
287 
288  QgsPaintEffect *effect = layer->paintEffect();
289  if ( effect && effect->enabled() )
290  {
291  QgsEffectPainter p( context.renderContext() );
292  p->translate( points.boundingRect().topLeft() );
293  p.setEffect( effect );
294  layer->renderPolyline( points.translated( -points.boundingRect().topLeft() ), context );
295  }
296  else
297  {
298  layer->renderPolyline( points, context );
299  }
300 }
301 
302 
304 {
305  QgsLineSymbol *cloneSymbol = new QgsLineSymbol( cloneLayers() );
306  cloneSymbol->setOpacity( mOpacity );
308  cloneSymbol->setLayer( mLayer );
311  cloneSymbol->setForceRHR( mForceRHR );
313  cloneSymbol->setFlags( mSymbolFlags );
315  return cloneSymbol;
316 }
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:72
@ Line
Line symbol.
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.
A class to manager painter saving and restoring required for effect drawing.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Qgis::SymbolType symbolType() const
Access the symbol type.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line's width.
void setOffset(double offset)
Sets the line's offset.
virtual double width() const
Returns the estimated width for the line symbol layer.
double offset() const
Returns the line's offset.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
void setDataDefinedWidth(const QgsProperty &property) const
Set data defined width for whole symbol (including all symbol layers).
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
void setWidth(double width) const
Sets the width for the whole line symbol.
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context.
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
void setWidthUnit(QgsUnitTypes::RenderUnit unit) const
Sets the width units for the whole symbol (including all symbol layers).
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
QgsLineSymbol(const QgsSymbolLayerList &layers=QgsSymbolLayerList())
Constructor for QgsLineSymbol, with the specified list of initial symbol layers.
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
QgsMapLayerType type
Definition: qgsmaplayer.h:80
Base class for visual effects which can be applied to QPicture drawings.
bool enabled() const
Returns whether the effect is enabled.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
A store for object properties.
Definition: qgsproperty.h:231
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
bool isSymbolLayerEnabled(const QgsSymbolLayer *layer) const
When rendering a map layer in a second pass (for selective masking), some symbol layers may be disabl...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleLineSymbolLayer, using the settings serialized in the properties map (correspo...
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyLayerEnabled
Whether symbol layer is enabled.
Qgis::SymbolType type() const
bool enabled() const
Returns true if symbol layer is enabled and will be drawn.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
void setGeometryPartCount(int count)
Sets the part count of current geometry.
void setGeometryPartNum(int num)
Sets the part number of current geometry.
void setOriginalGeometryType(QgsWkbTypes::GeometryType type)
Sets the geometry type for the original feature geometry being rendered.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol's property collection, used for data defined overrides.
Definition: qgssymbol.h:637
Qgis::SymbolFlags mSymbolFlags
Symbol flags.
Definition: qgssymbol.h:798
QgsSymbolLayerList cloneLayers() const
Retrieve a cloned list of all layers that make up this symbol.
Definition: qgssymbol.cpp:1118
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1795
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:727
@ PropertyOpacity
Opacity.
Definition: qgssymbol.h:131
void renderUsingLayer(QgsSymbolLayer *layer, QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::GeometryType::UnknownGeometry, const QPolygonF *points=nullptr, const QVector< QPolygonF > *rings=nullptr)
Renders a context using a particular symbol layer without passing in a geometry.
Definition: qgssymbol.cpp:1132
qreal mOpacity
Symbol opacity (in the range 0 - 1)
Definition: qgssymbol.h:789
Q_DECL_DEPRECATED const QgsVectorLayer * mLayer
Definition: qgssymbol.h:805
bool mClipFeaturesToExtent
Definition: qgssymbol.h:800
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:495
void setFlags(Qgis::SymbolFlags flags)
Sets flags for the symbol.
Definition: qgssymbol.h:522
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol's property collection, used for data defined overrides.
Definition: qgssymbol.h:622
void setOpacity(qreal opacity)
Sets the opacity for the symbol.
Definition: qgssymbol.h:502
Qgis::SymbolRenderHints mRenderHints
Definition: qgssymbol.h:791
bool mForceRHR
Definition: qgssymbol.h:801
QgsSymbolLayerList mLayers
Definition: qgssymbol.h:786
Q_DECL_DEPRECATED const QgsVectorLayer * layer() const
Definition: qgssymbol.cpp:1205
QgsSymbolAnimationSettings mAnimationSettings
Definition: qgssymbol.h:803
void setAnimationSettings(const QgsSymbolAnimationSettings &settings)
Sets a the symbol animation settings.
Definition: qgssymbol.cpp:670
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:1198
void setClipFeaturesToExtent(bool clipFeaturesToExtent)
Sets whether features drawn by the symbol should be clipped to the render context's extent.
Definition: qgssymbol.h:541
void setForceRHR(bool force)
Sets whether polygon features drawn by the symbol should be reoriented to follow the standard right-h...
Definition: qgssymbol.h:563
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:175
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2815
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2814
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27