QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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
19#include "qgslinesymbollayer.h"
20#include "qgspainteffect.h"
21#include "qgssymbollayerutils.h"
22
23std::unique_ptr< QgsLineSymbol > QgsLineSymbol::createSimple( const QVariantMap &properties )
24{
26 if ( !sl )
27 return nullptr;
28
29 QgsSymbolLayerList layers;
30 layers.append( sl );
31 return std::make_unique< QgsLineSymbol >( layers );
32}
33
35 : QgsSymbol( Qgis::SymbolType::Line, layers )
36{
37 if ( mLayers.isEmpty() )
38 mLayers.append( new QgsSimpleLineSymbolLayer() );
39}
40
41void QgsLineSymbol::setWidth( double w ) const
42{
43 const double origWidth = width();
44
45 const auto constMLayers = mLayers;
46 for ( QgsSymbolLayer *layer : constMLayers )
47 {
48 QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
49 if ( lineLayer )
50 {
51 if ( qgsDoubleNear( lineLayer->width(), origWidth ) )
52 {
53 lineLayer->setWidth( w );
54 }
55 else if ( !qgsDoubleNear( origWidth, 0.0 ) )
56 {
57 // proportionally scale the width
58 lineLayer->setWidth( lineLayer->width() * w / origWidth );
59 }
60 // also scale offset to maintain relative position
61 if ( !qgsDoubleNear( origWidth, 0.0 ) && !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
62 lineLayer->setOffset( lineLayer->offset() * w / origWidth );
63 }
64 else
65 {
66 QgsGeometryGeneratorSymbolLayer *geomGeneratorLayer = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
67 if ( geomGeneratorLayer && geomGeneratorLayer->symbolType() == Qgis::SymbolType::Line )
68 {
69 QgsLineSymbol *lineSymbol = qgis::down_cast<QgsLineSymbol *>( geomGeneratorLayer->subSymbol() );
70 if ( qgsDoubleNear( lineSymbol->width(), origWidth ) )
71 {
72 lineSymbol->setWidth( w );
73 }
74 else if ( !qgsDoubleNear( origWidth, 0.0 ) )
75 {
76 // proportionally scale the width
77 lineSymbol->setWidth( lineSymbol->width() * w / origWidth );
78 }
79 }
80 }
81 }
82}
83
85{
86 const auto constLLayers = mLayers;
87 for ( QgsSymbolLayer *layer : constLLayers )
88 {
89 if ( layer->type() != Qgis::SymbolType::Line )
90 continue;
91
92 QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( layer );
93 lineLayer->setWidthUnit( unit );
94 }
95}
96
98{
99 double maxWidth = 0;
100 if ( mLayers.isEmpty() )
101 return maxWidth;
102
103 const auto constMLayers = mLayers;
104 for ( QgsSymbolLayer *symbolLayer : constMLayers )
105 {
106 const QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( symbolLayer );
107 if ( lineLayer )
108 {
109 const double width = lineLayer->width();
110 if ( width > maxWidth )
111 maxWidth = width;
112 }
113 else
114 {
115 QgsGeometryGeneratorSymbolLayer *geomGeneratorLayer = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
116 if ( geomGeneratorLayer && geomGeneratorLayer->symbolType() == Qgis::SymbolType::Line )
117 {
118 QgsLineSymbol *lineSymbol = qgis::down_cast<QgsLineSymbol *>( geomGeneratorLayer->subSymbol() );
119 const double width = lineSymbol->width();
120 if ( width > maxWidth )
121 maxWidth = width;
122 }
123 }
124 }
125 return maxWidth;
126}
127
128double QgsLineSymbol::width( const QgsRenderContext &context ) const
129{
130 // return width of the largest symbol
131 double maxWidth = 0;
132 for ( QgsSymbolLayer *layer : mLayers )
133 {
134 if ( layer->type() != Qgis::SymbolType::Line )
135 continue;
136 const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
137 const double layerWidth = lineLayer->width( context );
138 maxWidth = std::max( maxWidth, layerWidth );
139 }
140 return maxWidth;
141}
142
144{
145 const double symbolWidth = width();
146
147 const auto constMLayers = mLayers;
148 for ( QgsSymbolLayer *layer : constMLayers )
149 {
150 QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
151
152 if ( lineLayer )
153 {
154 if ( !property )
155 {
158 }
159 else
160 {
161 if ( qgsDoubleNear( symbolWidth, 0.0 ) || qgsDoubleNear( lineLayer->width(), symbolWidth ) )
162 {
164 }
165 else
166 {
167 lineLayer->setDataDefinedProperty( QgsSymbolLayer::Property::StrokeWidth, QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->width() / symbolWidth, property ) );
168 }
169
170 if ( !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
171 {
172 lineLayer->setDataDefinedProperty( QgsSymbolLayer::Property::Offset, QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->offset() / symbolWidth, property ) );
173 }
174 }
175 }
176 }
177}
178
180{
181 const double symbolWidth = width();
182
183 QgsProperty symbolDD;
184
185 // find the base of the "en masse" pattern
186 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
187 {
188 const QgsLineSymbolLayer *layer = dynamic_cast<const QgsLineSymbolLayer *>( *it );
189 if ( layer && qgsDoubleNear( layer->width(), symbolWidth ) && layer->dataDefinedProperties().isActive( QgsSymbolLayer::Property::StrokeWidth ) )
190 {
191 symbolDD = layer->dataDefinedProperties().property( QgsSymbolLayer::Property::StrokeWidth );
192 break;
193 }
194 }
195
196 if ( !symbolDD )
197 return QgsProperty();
198
199 // check that all layers width expressions match the "en masse" pattern
200 const auto constMLayers = mLayers;
201 for ( QgsSymbolLayer *layer : constMLayers )
202 {
203 if ( layer->type() != Qgis::SymbolType::Line )
204 continue;
205 const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
206
209
210 if ( qgsDoubleNear( lineLayer->width(), symbolWidth ) )
211 {
212 if ( !layerWidthDD || layerWidthDD != symbolDD )
213 return QgsProperty();
214 }
215 else
216 {
217 if ( qgsDoubleNear( symbolWidth, 0.0 ) )
218 return QgsProperty();
219
220 const QgsProperty scaledDD( QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->width() / symbolWidth, symbolDD ) );
221 if ( !layerWidthDD || layerWidthDD != scaledDD )
222 return QgsProperty();
223 }
224
225 const QgsProperty scaledOffsetDD( QgsSymbolLayerUtils::scaleWholeSymbol( lineLayer->offset() / symbolWidth, symbolDD ) );
226 if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
227 return QgsProperty();
228 }
229
230 return symbolDD;
231}
232
233void QgsLineSymbol::renderPolyline( const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
234{
236 : mOpacity;
237
238 //save old painter
239 QPainter *renderPainter = context.painter();
240
241 QgsSymbolRenderContext symbolContext( context, Qgis::RenderUnit::Unknown, opacity, selected, renderHints(), f );
243 symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
244 symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
245
246 if ( layerIdx != -1 )
247 {
248 QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
249 if ( symbolLayer && symbolLayer->enabled() && context.isSymbolLayerEnabled( symbolLayer ) )
250 {
251 if ( symbolLayer->type() == Qgis::SymbolType::Line )
252 {
253 QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
254 renderPolylineUsingLayer( lineLayer, points, symbolContext );
255 }
256 else
257 renderUsingLayer( symbolLayer, symbolContext, Qgis::GeometryType::Line, &points );
258 }
259 return;
260 }
261
262 const auto constMLayers = mLayers;
263 for ( QgsSymbolLayer *symbolLayer : constMLayers )
264 {
265 if ( context.renderingStopped() )
266 break;
267
268 if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
269 continue;
270
271 if ( symbolLayer->type() == Qgis::SymbolType::Line )
272 {
273 QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
274 renderPolylineUsingLayer( lineLayer, points, symbolContext );
275 }
276 else
277 {
278 renderUsingLayer( symbolLayer, symbolContext, Qgis::GeometryType::Line, &points );
279 }
280 }
281
282 context.setPainter( renderPainter );
283}
284
285void QgsLineSymbol::renderPolylineUsingLayer( QgsLineSymbolLayer *layer, const QPolygonF &points, QgsSymbolRenderContext &context )
286{
287 if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::Property::LayerEnabled, context.renderContext().expressionContext(), true ) )
288 return;
289
290 QgsPaintEffect *effect = layer->paintEffect();
291 if ( effect && effect->enabled() )
292 {
293 QgsEffectPainter p( context.renderContext() );
294 p->translate( points.boundingRect().topLeft() );
295 p.setEffect( effect );
296 layer->renderPolyline( points.translated( -points.boundingRect().topLeft() ), context );
297 }
298 else
299 {
300 layer->renderPolyline( points, context );
301 }
302}
303
304
306{
307 QgsLineSymbol *cloneSymbol = new QgsLineSymbol( cloneLayers() );
308 cloneSymbol->copyCommonProperties( this );
309 return cloneSymbol;
310}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:56
@ Line
Lines.
Definition qgis.h:360
RenderUnit
Rendering size units.
Definition qgis.h:5183
@ Unknown
Mixed or unknown units.
Definition qgis.h:5190
@ Line
Line symbol.
Definition qgis.h:612
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.
Manages 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:58
A symbol layer subclass which alters rendered feature shapes through the use of QGIS expressions.
Qgis::SymbolType symbolType() const
Access the symbol type.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
Abstract base class for line symbol layers.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffset(double offset)
Sets the line's offset.
void setWidthUnit(Qgis::RenderUnit unit)
Sets the units for the line's width.
virtual double width() const
Returns the estimated width for the line symbol layer.
double offset() const
Returns the line's offset.
void setWidthUnit(Qgis::RenderUnit unit) const
Sets the width units for the whole symbol (including all symbol layers).
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.
static std::unique_ptr< QgsLineSymbol > createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
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).
Base class for visual effects which can be applied to QPicture drawings.
bool enabled() const
Returns whether the effect is enabled.
bool hasActiveProperties() const final
Returns true if the collection has any active properties, or false if all properties within the colle...
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A store for object properties.
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...
Abstract base class for symbol layers.
@ LayerEnabled
Whether symbol layer is enabled.
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.
Encapsulates the context in which a symbol is being rendered.
void setOriginalGeometryType(Qgis::GeometryType type)
Sets the geometry type for the original feature geometry being rendered.
void setGeometryPartCount(int count)
Sets the part count of current geometry.
void setGeometryPartNum(int num)
Sets the part number of current geometry.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
void renderUsingLayer(QgsSymbolLayer *layer, QgsSymbolRenderContext &context, Qgis::GeometryType geometryType=Qgis::GeometryType::Unknown, const QPolygonF *points=nullptr, const QVector< QPolygonF > *rings=nullptr)
Renders a context using a particular symbol layer without passing in a geometry.
QgsSymbolLayerList cloneLayers() const
Retrieve a cloned list of all layers that make up this symbol.
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void copyCommonProperties(const QgsSymbol *other)
Copies common properties from an other symbol to this symbol.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol's property collection, used for data defined overrides.
Definition qgssymbol.h:814
qreal mOpacity
Symbol opacity (in the range 0 - 1).
Definition qgssymbol.h:1028
qreal opacity() const
Returns the opacity for the symbol.
Definition qgssymbol.h:659
QgsSymbolLayerList mLayers
Definition qgssymbol.h:1022
Q_DECL_DEPRECATED const QgsVectorLayer * layer() const
QgsSymbol(Qgis::SymbolType type, const QgsSymbolLayerList &layers)
Constructor for a QgsSymbol of the specified type.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30