QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsgeometrygeneratorsymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometrygeneratorsymbollayer.cpp
3  ---------------------
4  begin : November 2015
5  copyright : (C) 2015 by Matthias Kuhn
6  email : matthias at opengis dot ch
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 
17 #include "qgsgeometry.h"
18 #include "qgsmarkersymbol.h"
19 #include "qgslinesymbol.h"
20 #include "qgsfillsymbol.h"
21 #include "qgspolygon.h"
22 
24 
26 
28 {
29  QString expression = properties.value( QStringLiteral( "geometryModifier" ) ).toString();
30  if ( expression.isEmpty() )
31  {
32  expression = QStringLiteral( "$geometry" );
33  }
35 
36  if ( properties.value( QStringLiteral( "SymbolType" ) ) == QLatin1String( "Marker" ) )
37  {
39  }
40  else if ( properties.value( QStringLiteral( "SymbolType" ) ) == QLatin1String( "Line" ) )
41  {
43  }
44  else
45  {
47  }
48  symbolLayer->setUnits( QgsUnitTypes::decodeRenderUnit( properties.value( QStringLiteral( "units" ), QStringLiteral( "mapunits" ) ).toString() ) );
49 
51 
52  return symbolLayer;
53 }
54 
55 QgsGeometryGeneratorSymbolLayer::QgsGeometryGeneratorSymbolLayer( const QString &expression )
56  : QgsSymbolLayer( Qgis::SymbolType::Hybrid )
57  , mExpression( new QgsExpression( expression ) )
58  , mSymbolType( Qgis::SymbolType::Marker )
59 {
60 
61 }
62 
64 {
65  return QStringLiteral( "GeometryGenerator" );
66 }
67 
69 {
71  {
72  if ( !mFillSymbol )
73  mFillSymbol.reset( QgsFillSymbol::createSimple( QVariantMap() ) );
74  mSymbol = mFillSymbol.get();
75  }
76  else if ( symbolType == Qgis::SymbolType::Line )
77  {
78  if ( !mLineSymbol )
79  mLineSymbol.reset( QgsLineSymbol::createSimple( QVariantMap() ) );
80  mSymbol = mLineSymbol.get();
81  }
83  {
84  if ( !mMarkerSymbol )
85  mMarkerSymbol.reset( QgsMarkerSymbol::createSimple( QVariantMap() ) );
86  mSymbol = mMarkerSymbol.get();
87  }
88  else
89  Q_ASSERT( false );
90 
91  mSymbolType = symbolType;
92 }
93 
95 {
96  mExpression->prepare( &context.renderContext().expressionContext() );
97 
98  subSymbol()->startRender( context.renderContext() );
99 }
100 
102 {
103  if ( mSymbol )
104  mSymbol->stopRender( context.renderContext() );
105 }
106 
108 {
109  mRenderingFeature = true;
110  mHasRenderedFeature = false;
111 }
112 
114 {
115  mRenderingFeature = false;
116 }
117 
119 {
120  if ( mFillSymbol )
121  return mFillSymbol->usesMapUnits();
122  else if ( mLineSymbol )
123  return mLineSymbol->usesMapUnits();
124  else if ( mMarkerSymbol )
125  return mMarkerSymbol->usesMapUnits();
126  return false;
127 }
128 
130 {
131  if ( mFillSymbol )
132  return mFillSymbol->color();
133  else if ( mLineSymbol )
134  return mLineSymbol->color();
135  else if ( mMarkerSymbol )
136  return mMarkerSymbol->color();
137  return QColor();
138 }
139 
141 {
142  if ( mFillSymbol )
143  return mFillSymbol->outputUnit();
144  else if ( mLineSymbol )
145  return mLineSymbol->outputUnit();
146  else if ( mMarkerSymbol )
147  return mMarkerSymbol->outputUnit();
149 }
150 
152 {
153  if ( mFillSymbol )
154  return mFillSymbol->mapUnitScale();
155  else if ( mLineSymbol )
156  return mLineSymbol->mapUnitScale();
157  else if ( mMarkerSymbol )
158  return mMarkerSymbol->mapUnitScale();
159  return QgsMapUnitScale();
160 }
161 
163 {
164  QgsGeometryGeneratorSymbolLayer *clone = new QgsGeometryGeneratorSymbolLayer( mExpression->expression() );
165 
166  if ( mFillSymbol )
167  clone->mFillSymbol.reset( mFillSymbol->clone() );
168  if ( mLineSymbol )
169  clone->mLineSymbol.reset( mLineSymbol->clone() );
170  if ( mMarkerSymbol )
171  clone->mMarkerSymbol.reset( mMarkerSymbol->clone() );
172 
173  clone->setSymbolType( mSymbolType );
174  clone->setUnits( mUnits );
175 
178 
179  return clone;
180 }
181 
183 {
184  QVariantMap props;
185  props.insert( QStringLiteral( "geometryModifier" ), mExpression->expression() );
186  switch ( mSymbolType )
187  {
189  props.insert( QStringLiteral( "SymbolType" ), QStringLiteral( "Marker" ) );
190  break;
192  props.insert( QStringLiteral( "SymbolType" ), QStringLiteral( "Line" ) );
193  break;
194  default:
195  props.insert( QStringLiteral( "SymbolType" ), QStringLiteral( "Fill" ) );
196  break;
197  }
198  props.insert( QStringLiteral( "units" ), QgsUnitTypes::encodeUnit( mUnits ) );
199 
200  return props;
201 }
202 
204 {
205  if ( mSymbol )
206  mSymbol->drawPreviewIcon( context.renderContext().painter(), size, nullptr, false, nullptr, context.patchShape() );
207 }
208 
210 {
211  mExpression.reset( new QgsExpression( exp ) );
212 }
213 
215 {
216  switch ( symbol->type() )
217  {
219  mMarkerSymbol.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
220  break;
221 
223  mLineSymbol.reset( static_cast<QgsLineSymbol *>( symbol ) );
224  break;
225 
227  mFillSymbol.reset( static_cast<QgsFillSymbol *>( symbol ) );
228  break;
229 
230  default:
231  break;
232  }
233 
234  setSymbolType( symbol->type() );
235 
236  return true;
237 }
238 
240 {
241  return QgsSymbolLayer::usedAttributes( context )
242  + mSymbol->usedAttributes( context )
243  + mExpression->referencedColumns();
244 }
245 
247 {
248  // we treat geometry generator layers like they have data defined properties,
249  // since the WHOLE layer is based on expressions and requires the full expression
250  // context
251  return true;
252 }
253 
255 {
256  Q_UNUSED( symbol )
257  return true;
258 }
259 void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
260 {
261  if ( mRenderingFeature && mHasRenderedFeature )
262  return;
263 
264  QgsExpressionContext &expressionContext = context.renderContext().expressionContext();
265  QgsFeature f = expressionContext.feature();
266 
267  if ( !context.feature() && points )
268  {
269  // oh dear, we don't have a feature to work from... but that's ok, we are probably being rendered as a plain old symbol!
270  // in this case we need to build up a feature which represents the points being rendered
271  QgsGeometry drawGeometry;
272 
273  // step 1 - convert points and rings to geometry
274  switch ( geometryType )
275  {
277  {
278  Q_ASSERT( points->size() == 1 );
279  drawGeometry = QgsGeometry::fromPointXY( points->at( 0 ) );
280  break;
281  }
283  {
284  Q_ASSERT( !rings );
285  std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( *points ) );
286  drawGeometry = QgsGeometry( std::move( ring ) );
287  break;
288  }
290  {
291  std::unique_ptr < QgsLineString > exterior( QgsLineString::fromQPolygonF( *points ) );
292  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
293  polygon->setExteriorRing( exterior.release() );
294  if ( rings )
295  {
296  for ( const QPolygonF &ring : *rings )
297  {
298  polygon->addInteriorRing( QgsLineString::fromQPolygonF( ring ) );
299  }
300  }
301  drawGeometry = QgsGeometry( std::move( polygon ) );
302  break;
303  }
304 
307  return; // unreachable
308  }
309 
310  // step 2 - scale the draw geometry from PAINTER units to target units (e.g. millimeters)
311  const double scale = 1 / context.renderContext().convertToPainterUnits( 1, mUnits );
312  const QTransform painterToTargetUnits = QTransform::fromScale( scale, scale );
313  drawGeometry.transform( painterToTargetUnits );
314 
315  // step 3 - set the feature to use the new scaled geometry, and inject it into the expression context
316  f.setGeometry( drawGeometry );
318  QgsExpressionContextScopePopper popper( expressionContext, generatorScope );
319  generatorScope->setFeature( f );
320 
321  // step 4 - evaluate the new generated geometry.
322  QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
323 
324  // step 5 - transform geometry back from target units to MAP units. We transform to map units here
325  // as we'll ultimately be calling renderFeature, which excepts the feature has a geometry in map units.
326  // Here we also scale the transform by the target unit to painter units factor to reverse that conversion
327  geom.transform( painterToTargetUnits.inverted( ) );
328  QTransform mapToPixel = context.renderContext().mapToPixel().transform();
329  geom.transform( mapToPixel.inverted() );
330  // also need to apply the coordinate transform from the render context
331  try
332  {
333  geom.transform( context.renderContext().coordinateTransform(), Qgis::TransformDirection::Reverse );
334  }
335  catch ( QgsCsException & )
336  {
337  QgsDebugMsg( QStringLiteral( "Could no transform generated geometry to layer CRS" ) );
338  }
339 
340  f.setGeometry( geom );
341  }
342  else if ( context.feature() )
343  {
344  switch ( mUnits )
345  {
347  case QgsUnitTypes::RenderUnknownUnit: // unsupported, not exposed as an option
348  case QgsUnitTypes::RenderMetersInMapUnits: // unsupported, not exposed as an option
349  case QgsUnitTypes::RenderPercentage: // unsupported, not exposed as an option
350  {
351  QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
352  f.setGeometry( geom );
353  break;
354  }
355 
360  {
361  // add a new scope for the transformed geometry
363  QgsExpressionContextScopePopper popper( expressionContext, generatorScope );
364 
365  QgsGeometry transformed = f.geometry();
366  transformed.transform( context.renderContext().coordinateTransform() );
367  QTransform mapToPixel = context.renderContext().mapToPixel().transform();
368 
369  // scale transform to target units
370  const double scale = 1 / context.renderContext().convertToPainterUnits( 1, mUnits );
371  mapToPixel.scale( scale, scale );
372 
373  transformed.transform( mapToPixel );
374  f.setGeometry( transformed );
375  generatorScope->setFeature( f );
376 
377  QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
378 
379  // transform geometry back from screen units to layer crs
380  geom.transform( mapToPixel.inverted() );
381  geom.transform( context.renderContext().coordinateTransform(), Qgis::TransformDirection::Reverse );
382 
383  f.setGeometry( geom );
384  break;
385  }
386  }
387  }
388 
389  QgsExpressionContextScope *subSymbolExpressionContextScope = mSymbol->symbolRenderContext()->expressionContextScope();
390 
391  subSymbolExpressionContextScope->setFeature( f );
392 
393  mSymbol->renderFeature( f, context.renderContext(), -1, context.selected() );
394 
395  if ( mRenderingFeature )
396  mHasRenderedFeature = true;
397 }
398 
399 void QgsGeometryGeneratorSymbolLayer::setColor( const QColor &color )
400 {
401  mSymbol->setColor( color );
402 }
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:63
SymbolType
Symbol types.
Definition: qgis.h:169
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
Class for parsing and evaluation of expressions (formerly called "search strings").
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsMapUnitScale mapUnitScale() const override
void render(QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::GeometryType::UnknownGeometry, const QPolygonF *points=nullptr, const QVector< QPolygonF > *rings=nullptr)
Will render this symbol layer using the context.
void drawPreviewIcon(QgsSymbolRenderContext &context, QSize size) override
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void setGeometryExpression(const QString &exp)
Set the expression to generate this geometry.
void setSymbolType(Qgis::SymbolType symbolType)
Set the type of symbol which should be created.
QString layerType() const override
Returns a string that represents this layer type.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
bool isCompatibleWithSymbol(QgsSymbol *symbol) const override
Will always return true.
Qgis::SymbolType symbolType() const
Access the symbol type.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setUnits(QgsUnitTypes::RenderUnit units)
Sets the units for the geometry expression.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setColor(const QColor &color) override
The fill color.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsSymbolLayer * create(const QVariantMap &properties)
Creates the symbol layer.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
QgsSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QColor color() const override
The fill color.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
Struct for storing maximum and minimum scales for measurements in map units.
A marker symbol type, for rendering Point and MultiPoint geometries.
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified 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.
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).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsExpressionContextScope * expressionContextScope()
This scope is always available when a symbol of this type is being rendered.
const QgsLegendPatchShape * patchShape() const
Returns the symbol patch shape, to use if rendering symbol preview icons.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1444
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:516
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter.
Definition: qgssymbol.cpp:562
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:802
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:541
void renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false, Qgis::VertexMarkerType currentVertexMarkerType=Qgis::VertexMarkerType::SemiTransparentCircle, double currentVertexMarkerSize=0.0) SIP_THROW(QgsCsException)
Render a feature.
Definition: qgssymbol.cpp:903
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:489
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
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:171
@ RenderInches
Inches.
Definition: qgsunittypes.h:174
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
#define QgsDebugMsg(str)
Definition: qgslogger.h:38