QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsvectorfieldsymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorfieldsymbollayer.cpp
3  -----------------------------
4  begin : Octorer 25, 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgsvectorlayer.h"
20 #include "qgsunittypes.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgslinesymbol.h"
23 
25 {
26  setSubSymbol( new QgsLineSymbol() );
27 }
28 
30 
32 {
34  mDistanceUnit = unit;
35 }
36 
38 {
39  if ( QgsMarkerSymbolLayer::outputUnit() == mDistanceUnit )
40  {
41  return mDistanceUnit;
42  }
44 }
45 
47 {
49  mDistanceMapUnitScale = scale;
50 }
51 
53 {
54  if ( QgsMarkerSymbolLayer::mapUnitScale() == mDistanceMapUnitScale )
55  {
56  return mDistanceMapUnitScale;
57  }
58  return QgsMapUnitScale();
59 }
60 
61 QgsSymbolLayer *QgsVectorFieldSymbolLayer::create( const QVariantMap &properties )
62 {
64  if ( properties.contains( QStringLiteral( "x_attribute" ) ) )
65  {
66  symbolLayer->setXAttribute( properties[QStringLiteral( "x_attribute" )].toString() );
67  }
68  if ( properties.contains( QStringLiteral( "y_attribute" ) ) )
69  {
70  symbolLayer->setYAttribute( properties[QStringLiteral( "y_attribute" )].toString() );
71  }
72  if ( properties.contains( QStringLiteral( "distance_unit" ) ) )
73  {
74  symbolLayer->setDistanceUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "distance_unit" )].toString() ) );
75  }
76  if ( properties.contains( QStringLiteral( "distance_map_unit_scale" ) ) )
77  {
78  symbolLayer->setDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "distance_map_unit_scale" )].toString() ) );
79  }
80  if ( properties.contains( QStringLiteral( "scale" ) ) )
81  {
82  symbolLayer->setScale( properties[QStringLiteral( "scale" )].toDouble() );
83  }
84  if ( properties.contains( QStringLiteral( "vector_field_type" ) ) )
85  {
86  symbolLayer->setVectorFieldType( static_cast< VectorFieldType >( properties[QStringLiteral( "vector_field_type" )].toInt() ) );
87  }
88  if ( properties.contains( QStringLiteral( "angle_orientation" ) ) )
89  {
90  symbolLayer->setAngleOrientation( static_cast< AngleOrientation >( properties[QStringLiteral( "angle_orientation" )].toInt() ) );
91  }
92  if ( properties.contains( QStringLiteral( "angle_units" ) ) )
93  {
94  symbolLayer->setAngleUnits( static_cast< AngleUnits >( properties[QStringLiteral( "angle_units" )].toInt() ) );
95  }
96  if ( properties.contains( QStringLiteral( "size" ) ) )
97  {
98  symbolLayer->setSize( properties[QStringLiteral( "size" )].toDouble() );
99  }
100  if ( properties.contains( QStringLiteral( "size_unit" ) ) )
101  {
102  symbolLayer->setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
103  }
104  if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
105  {
106  symbolLayer->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
107  }
108  if ( properties.contains( QStringLiteral( "offset" ) ) )
109  {
110  symbolLayer->setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
111  }
112  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
113  {
114  symbolLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
115  }
116  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
117  {
118  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
119  }
120  return symbolLayer;
121 }
122 
124 {
125  if ( symbol->type() == Qgis::SymbolType::Line )
126  {
127  mLineSymbol.reset( static_cast<QgsLineSymbol *>( symbol ) );
128  return true;
129  }
130  return false;
131 }
132 
134 {
135  return mLineSymbol.get();
136 }
137 
139 {
140  if ( !mLineSymbol )
141  {
142  return;
143  }
144 
145  const QgsRenderContext &ctx = context.renderContext();
146 
147  const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
149 
150  if ( !context.feature() )
151  {
152  //preview
153  QPolygonF line;
154  line << QPointF( 0, 50 );
155  line << QPointF( 100, 50 );
156  mLineSymbol->renderPolyline( line, nullptr, context.renderContext() );
158  return;
159  }
160 
161  const QgsFeature f( *context.feature() );
162 
163  double xComponent = 0;
164  double yComponent = 0;
165 
166  double xVal = 0;
167  if ( mXIndex != -1 )
168  {
169  xVal = f.attribute( mXIndex ).toDouble();
170  }
171  double yVal = 0;
172  if ( mYIndex != -1 )
173  {
174  yVal = f.attribute( mYIndex ).toDouble();
175  }
176 
177  const QgsMapToPixel &m2p = ctx.mapToPixel();
178  const double mapRotation = m2p.mapRotation();
179 
180  QPolygonF line;
181  line << point;
182 
183  QPointF destPoint;
184  switch ( mVectorFieldType )
185  {
186  case Cartesian:
187  {
188  destPoint = QPointF( point.x() + mScale * ctx.convertToPainterUnits( xVal, mDistanceUnit, mDistanceMapUnitScale ),
189  point.y() - mScale * ctx.convertToPainterUnits( yVal, mDistanceUnit, mDistanceMapUnitScale ) );
190  break;
191  }
192 
193  case Polar:
194  {
195  convertPolarToCartesian( xVal, yVal, xComponent, yComponent );
196  destPoint = QPointF( point.x() + mScale * ctx.convertToPainterUnits( xComponent, mDistanceUnit, mDistanceMapUnitScale ),
197  point.y() - mScale * ctx.convertToPainterUnits( yComponent, mDistanceUnit, mDistanceMapUnitScale ) );
198  break;
199  }
200 
201  case Height:
202  {
203  destPoint = QPointF( point.x(), point.y() - ( mScale * ctx.convertToPainterUnits( yVal, mDistanceUnit, mDistanceMapUnitScale ) ) );
204  break;
205  }
206  }
207 
208  if ( !qgsDoubleNear( mapRotation, 0.0 ) && mVectorFieldType != Height )
209  {
210  const double radians = mapRotation * M_PI / 180.0;
211  destPoint = QPointF( cos( radians ) * ( destPoint.x() - point.x() ) - sin( radians ) * ( destPoint.y() - point.y() ) + point.x(),
212  sin( radians ) * ( destPoint.x() - point.x() ) + cos( radians ) * ( destPoint.y() - point.y() ) + point.y() );
213  }
214 
215  line << destPoint;
216 
217  mLineSymbol->renderPolyline( line, &f, context.renderContext() );
219 }
220 
222 {
223  if ( mLineSymbol )
224  {
225  mLineSymbol->startRender( context.renderContext(), context.fields() );
226  }
227 
228  const QgsFields fields = context.fields();
229  if ( !fields.isEmpty() )
230  {
231  mXIndex = fields.lookupField( mXAttribute );
232  mYIndex = fields.lookupField( mYAttribute );
233  }
234  else
235  {
236  mXIndex = -1;
237  mYIndex = -1;
238  }
239 }
240 
242 {
243  if ( mLineSymbol )
244  {
245  mLineSymbol->stopRender( context.renderContext() );
246  }
247 }
248 
250 {
252  if ( mLineSymbol )
253  {
254  clonedLayer->setSubSymbol( mLineSymbol->clone() );
255  }
256  return static_cast< QgsVectorFieldSymbolLayer * >( clonedLayer );
257 }
258 
260 {
261  QVariantMap properties;
262  properties[QStringLiteral( "x_attribute" )] = mXAttribute;
263  properties[QStringLiteral( "y_attribute" )] = mYAttribute;
264  properties[QStringLiteral( "distance_unit" )] = QgsUnitTypes::encodeUnit( mDistanceUnit );
265  properties[QStringLiteral( "distance_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceMapUnitScale );
266  properties[QStringLiteral( "scale" )] = QString::number( mScale );
267  properties[QStringLiteral( "vector_field_type" )] = QString::number( mVectorFieldType );
268  properties[QStringLiteral( "angle_orientation" )] = QString::number( mAngleOrientation );
269  properties[QStringLiteral( "angle_units" )] = QString::number( mAngleUnits );
270  properties[QStringLiteral( "size" )] = QString::number( mSize );
271  properties[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
272  properties[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
273  properties[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
274  properties[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
275  properties[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
276  return properties;
277 }
278 
280 {
281  return mDistanceUnit == QgsUnitTypes::RenderMapUnits || mDistanceUnit == QgsUnitTypes::RenderMetersInMapUnits
284 }
285 
286 void QgsVectorFieldSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
287 {
288  element.appendChild( doc.createComment( QStringLiteral( "VectorField not implemented yet..." ) ) );
289  mLineSymbol->toSld( doc, element, props );
290 }
291 
293 {
294  Q_UNUSED( element )
295  return nullptr;
296 }
297 
299 {
300  if ( mLineSymbol )
301  {
302  mLineSymbol->drawPreviewIcon( context.renderContext().painter(), size );
303  }
304 }
305 
306 QSet<QString> QgsVectorFieldSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
307 {
308  QSet<QString> attributes = QgsMarkerSymbolLayer::usedAttributes( context );
309  if ( !mXAttribute.isEmpty() )
310  {
311  attributes.insert( mXAttribute );
312  }
313  if ( !mYAttribute.isEmpty() )
314  {
315  attributes.insert( mYAttribute );
316  }
317  if ( mLineSymbol )
318  {
319  attributes.unite( mLineSymbol->usedAttributes( context ) );
320  }
321  return attributes;
322 }
323 
325 {
327  return true;
328  if ( mLineSymbol && mLineSymbol->hasDataDefinedProperties() )
329  return true;
330  return false;
331 }
332 
333 void QgsVectorFieldSymbolLayer::convertPolarToCartesian( double length, double angle, double &x, double &y ) const
334 {
335  //convert angle to degree and to north orientation
336  if ( mAngleOrientation == CounterclockwiseFromEast )
337  {
338  if ( angle <= 90 )
339  {
340  angle = 90 - angle;
341  }
342  else
343  {
344  angle = 360 - angle + 90;
345  }
346  }
347 
348  if ( mAngleUnits == Degrees )
349  {
350  angle = angle * M_PI / 180.0;
351  }
352 
353  x = length * std::sin( angle );
354  y = length * std::cos( angle );
355 }
356 
357 void QgsVectorFieldSymbolLayer::setColor( const QColor &color )
358 {
359  if ( mLineSymbol )
360  mLineSymbol->setColor( color );
361 
362  mColor = color;
363 }
364 
366 {
367  return mLineSymbol ? mLineSymbol->color() : mColor;
368 }
369 
370 
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ Line
Line symbol.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
double mSize
Marker size.
virtual void setSize(double size)
Sets the symbol size.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's offset.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
QgsFields fields() const
Fields of the layer.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
const QgsFeature * feature() const
Returns the current feature being rendered.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:175
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:176
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
A symbol layer class for displaying displacement arrows based on point layer attributes.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
QColor color() const override
The fill color.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setYAttribute(const QString &attribute)
void setAngleUnits(AngleUnits units)
void setVectorFieldType(VectorFieldType type)
void setDistanceUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the distance.
~QgsVectorFieldSymbolLayer() override
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setAngleOrientation(AngleOrientation orientation)
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QgsVectorFieldSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol layer.
QgsMapUnitScale mapUnitScale() const override
void setXAttribute(const QString &attribute)
void setDistanceMapUnitScale(const QgsMapUnitScale &scale)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setColor(const QColor &color) override
The fill color.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void drawPreviewIcon(QgsSymbolRenderContext &context, QSize size) override
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246