QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsvectorlayerdiagramprovider.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerdiagramprovider.cpp
3  --------------------------------------
4  Date : September 2015
5  Copyright : (C) 2015 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 
17 
18 #include "qgsgeometry.h"
19 #include "qgslabelsearchtree.h"
20 #include "qgsvectorlayer.h"
22 #include "diagram/qgsdiagram.h"
23 #include "qgsgeos.h"
24 
25 #include "feature.h"
26 #include "labelposition.h"
27 
29  : QgsAbstractLabelProvider( layer )
30  , mSettings( *layer->diagramLayerSettings() )
31  , mDiagRenderer( layer->diagramRenderer()->clone() )
32  , mFields( layer->fields() )
33  , mLayerCrs( layer->crs() )
34  , mSource( ownFeatureLoop ? new QgsVectorLayerFeatureSource( layer ) : nullptr )
35  , mOwnsSource( ownFeatureLoop )
36 {
37  init();
38 }
39 
40 
42 {
43  mName = mLayerId;
44  mPriority = 1 - mSettings.priority() / 10.0; // convert 0..10 --> 1..0
46 }
47 
48 
50 {
51  if ( mOwnsSource )
52  delete mSource;
53 
54  qDeleteAll( mFeatures );
55 
56  // renderer is owned by mSettings
57 }
58 
59 
61 {
62  if ( !mSource )
63  {
64  // we have created the provider with "own feature loop" == false
65  // so it is assumed that prepare() has been already called followed by registerFeature() calls
66  return mFeatures;
67  }
68 
69  QSet<QString> attributeNames;
70  if ( !prepare( context, attributeNames ) )
71  return QList<QgsLabelFeature *>();
72 
73  QgsRectangle layerExtent = context.extent();
76 
77  QgsFeatureRequest request;
78  request.setFilterRect( layerExtent );
79  request.setSubsetOfAttributes( attributeNames, mFields );
80  const QgsFeatureFilterProvider *featureFilterProvider = context.featureFilterProvider();
81  if ( featureFilterProvider )
82  {
83  featureFilterProvider->filterFeatures( qobject_cast<QgsVectorLayer *>( mLayer ), request );
84  }
85  QgsFeatureIterator fit = mSource->getFeatures( request );
86 
87  QgsFeature fet;
88  while ( fit.nextFeature( fet ) )
89  {
90  context.expressionContext().setFeature( fet );
91  registerFeature( fet, context );
92  }
93 
94  return mFeatures;
95 }
96 
97 
99 {
100 #if 1 // XXX strk
101  // features are pre-rotated but not scaled/translated,
102  // so we only disable rotation here. Ideally, they'd be
103  // also pre-scaled/translated, as suggested here:
104  // https://github.com/qgis/QGIS/issues/20071
105  QgsMapToPixel xform = context.mapToPixel();
106  xform.setMapRotation( 0, 0, 0 );
107 #else
108  const QgsMapToPixel &xform = context.mapToPixel();
109 #endif
110 
111  QgsDiagramLabelFeature *dlf = dynamic_cast<QgsDiagramLabelFeature *>( label->getFeaturePart()->feature() );
112 
113  QgsFeature feature;
114  feature.setFields( mFields );
115  feature.setValid( true );
116  feature.setId( label->getFeaturePart()->featureId() );
117  feature.setAttributes( dlf->attributes() );
118 
119  context.expressionContext().setFeature( feature );
120 
121  //calculate top-left point for diagram
122  //first, calculate the centroid of the label (accounts for PAL creating
123  //rotated labels when we do not want to draw the diagrams rotated)
124  double centerX = 0;
125  double centerY = 0;
126  for ( int i = 0; i < 4; ++i )
127  {
128  centerX += label->getX( i );
129  centerY += label->getY( i );
130  }
131  QgsPointXY outPt( centerX / 4.0, centerY / 4.0 );
132  //then, calculate the top left point for the diagram with this center position
133  QgsPointXY centerPt = xform.transform( outPt.x() - label->getWidth() / 2,
134  outPt.y() - label->getHeight() / 2 );
135 
136  mSettings.renderer()->renderDiagram( feature, context, centerPt.toQPointF(), mSettings.dataDefinedProperties() );
137 
138  //insert into label search tree to manipulate position interactively
139  mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false );
140 
141 }
142 
143 
144 bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames )
145 {
147  const QgsMapSettings &mapSettings = mEngine->mapSettings();
148 
149  if ( context.coordinateTransform().isValid() )
150  // this is context for layer rendering
152  else
153  {
154  // otherwise fall back to creating our own CT
156  }
157 
159 
160  bool result = s2.prepare( context.expressionContext() );
161 
162  //add attributes needed by the diagram renderer
163  attributeNames.unite( s2.referencedFields( context.expressionContext() ) );
164 
165  return result;
166 }
167 
168 
170 {
171  QgsLabelFeature *label = registerDiagram( feature, context, obstacleGeometry );
172  if ( label )
173  mFeatures << label;
174 }
175 
176 
178 {
179  const QgsMapSettings &mapSettings = mEngine->mapSettings();
180 
181  const QgsDiagramRenderer *dr = mSettings.renderer();
182  if ( dr )
183  {
184  QList<QgsDiagramSettings> settingList = dr->diagramSettings();
185  if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility )
186  {
187  double maxScale = settingList.at( 0 ).maximumScale;
188  if ( maxScale > 0 && context.rendererScale() < maxScale )
189  {
190  return nullptr;
191  }
192 
193  double minScale = settingList.at( 0 ).minimumScale;
194  if ( minScale > 0 && context.rendererScale() > minScale )
195  {
196  return nullptr;
197  }
198  }
199  }
200 
201  // data defined show diagram? check this before doing any other processing
203  return nullptr;
204 
205  // data defined obstacle?
207 
208  //convert geom to geos
209  QgsGeometry geom = feat.geometry();
210  QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() );
211  if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
212  {
213  //PAL features are prerotated, so extent also needs to be unrotated
214  extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
215  }
216 
217  geos::unique_ptr geomCopy;
218  std::unique_ptr<QgsGeometry> scopedPreparedGeom;
219  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), extentGeom ) )
220  {
221  scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), extentGeom ) ) );
222  QgsGeometry *preparedGeom = scopedPreparedGeom.get();
223  if ( preparedGeom->isNull() )
224  return nullptr;
225  geomCopy = QgsGeos::asGeos( *preparedGeom );
226  }
227  else
228  {
229  geomCopy = QgsGeos::asGeos( geom );
230  }
231 
232  if ( !geomCopy )
233  return nullptr; // invalid geometry
234 
235  geos::unique_ptr geosObstacleGeomClone;
236  std::unique_ptr<QgsGeometry> scopedObstacleGeom;
237  if ( isObstacle && !obstacleGeometry.isNull() && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom ) )
238  {
239  QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom );
240  geosObstacleGeomClone = QgsGeos::asGeos( preparedObstacleGeom );
241  }
242  else if ( mSettings.isObstacle() && !obstacleGeometry.isNull() )
243  {
244  geosObstacleGeomClone = QgsGeos::asGeos( obstacleGeometry );
245  }
246 
247  double diagramWidth = 0;
248  double diagramHeight = 0;
249  if ( dr )
250  {
251  QSizeF diagSize = dr->sizeMapUnits( feat, context );
252  if ( diagSize.isValid() )
253  {
254  diagramWidth = diagSize.width();
255  diagramHeight = diagSize.height();
256  }
257  }
258 
259  // feature to the layer
260  bool alwaysShow = mSettings.showAllDiagrams();
261  context.expressionContext().setOriginalValueVariable( alwaysShow );
263 
264  // new style data defined position
265  bool ddPos = false;
266  double ddPosX = 0.0;
267  double ddPosY = 0.0;
272  {
273  ddPosX = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionX, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() );
274  ddPosY = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionY, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() );
275 
276  ddPos = !std::isnan( ddPosX ) && !std::isnan( ddPosY );
277 
278  if ( ddPos )
279  {
281  if ( ct.isValid() && !ct.isShortCircuited() )
282  {
283  double z = 0;
284  ct.transformInPlace( ddPosX, ddPosY, z );
285  }
286  //data defined diagram position is always centered
287  ddPosX -= diagramWidth / 2.0;
288  ddPosY -= diagramHeight / 2.0;
289  }
290  }
291 
292  QgsDiagramLabelFeature *lf = new QgsDiagramLabelFeature( feat.id(), std::move( geomCopy ), QSizeF( diagramWidth, diagramHeight ) );
293  lf->setHasFixedPosition( ddPos );
294  lf->setFixedPosition( QgsPointXY( ddPosX, ddPosY ) );
295  lf->setHasFixedAngle( true );
296  lf->setFixedAngle( 0 );
297  lf->setAlwaysShow( alwaysShow );
298  lf->setIsObstacle( isObstacle );
299  if ( geosObstacleGeomClone )
300  {
301  lf->setObstacleGeometry( std::move( geosObstacleGeomClone ) );
302  }
303 
304  if ( dr )
305  {
306  //append the diagram attributes to lbl
307  lf->setAttributes( feat.attributes() );
308  }
309 
310  // data defined priority?
313  {
316  priorityD = qBound( 0.0, priorityD, 10.0 );
317  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
318  lf->setPriority( priorityD );
319  }
320 
321  // z-Index
322  double zIndex = mSettings.zIndex();
325  {
326  context.expressionContext().setOriginalValueVariable( zIndex );
328  }
329  lf->setZIndex( zIndex );
330 
331  // label distance
332  QgsPointXY ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 );
333  QgsPointXY ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 );
334  double dist = mSettings.distance();
335 
338  {
341  }
342 
343  dist *= ptOne.distance( ptZero );
344 
345  lf->setDistLabel( dist );
346  return lf;
347 }
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
static QgsGeometry prepareGeometry(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry(), bool mergeLines=false)
Prepares a geometry for registration with PAL.
void setCoordinateTransform(const QgsCoordinateTransform &transform)
Sets the coordinate transform associated with the layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Distance to diagram from feature.
double rendererScale() const
Returns the renderer map scale.
void setMapRotation(double degrees, double cx, double cy)
Set map rotation in degrees (clockwise)
QgsWeakMapLayerPointer mLayer
Weak pointer to source layer.
QgsPalLayerSettings::Placement mPlacement
Placement strategy.
QgsDiagramRenderer * mDiagRenderer
Diagram renderer instance (owned by mSettings)
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:162
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:151
bool isObstacle() const
Returns whether the feature associated with a diagram acts as an obstacle for other labels or diagram...
QgsCoordinateTransformContext transformContext() const
Returns the context&#39;s coordinate transform context, which stores various information regarding which ...
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...
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:118
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
const QgsLabelingEngine * mEngine
Associated labeling engine.
QgsCoordinateTransform coordinateTransform() const
Returns the coordinate transform associated with the layer, or an invalid transform if no transformat...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
double mPriority
Default priority of labels.
void init()
initialization method - called from constructors
QgsDiagramRenderer * renderer()
Returns the diagram renderer associated with the layer.
int priority() const
Returns the diagram priority.
double getY(int i=0) const
Returns the down-left y coordinate.
double y
Definition: qgspointxy.h:48
bool hasProperty(int key) const override
Returns true if the collection contains a property with the specified key.
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the diagram&#39;s property collection, used for data defined overrides.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())=0
Gets an iterator for features matching the specified request.
QgsVectorLayerDiagramProvider(QgsVectorLayer *layer, bool ownFeatureLoop=true)
Convenience constructor to initialize the provider from given vector layer.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const
Returns the set of any fields referenced by the layer&#39;s diagrams.
FeaturePart * getFeaturePart()
Returns the feature corresponding to this labelposition.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties=QgsPropertyCollection()) const
Renders the diagram for a specified feature at a specific position in the passed render context...
const QgsCoordinateReferenceSystem & crs
Whether diagram features act as obstacles for other diagrams/labels.
void setAlwaysShow(bool enabled)
Sets whether label should be always shown (sets very high label priority)
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
virtual bool prepare(const QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
const QgsFeatureFilterProvider * featureFilterProvider() const
Gets the filter feature provider used for additional filtering of rendered features.
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
QList< QgsLabelFeature * > mFeatures
List of generated label features (owned by the provider)
void setHasFixedPosition(bool enabled)
Sets whether the label should use a fixed position instead of being automatically placed...
The QgsMapSettings class contains configuration for rendering of the map.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Z-index for diagram ordering.
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:154
QgsLabelFeature * registerDiagram(QgsFeature &feat, QgsRenderContext &context, const QgsGeometry &obstacleGeometry=QgsGeometry())
helper method to register one diagram feature
bool mOwnsSource
Whether layer&#39;s feature source is owned.
void setIsObstacle(bool enabled)
Sets whether the feature will act as an obstacle for labels.
void setZIndex(double zIndex)
Sets the label&#39;s z-index.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
Evaluates and returns the diagram settings relating to a diagram for a specific feature.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
static bool geometryRequiresPreparation(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry(), bool mergeLines=false)
Checks whether a geometry requires preparation before registration with PAL.
double getHeight() const
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const
Prepares the diagrams for a specified expression context.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
void setFixedAngle(double angle)
Sets angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const override
Draw this label at the position determined by the labeling engine.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Abstract interface for use by classes that filter the features of a layer.
Y-coordinate data defined diagram position.
void setDistLabel(double dist)
Applies to "around point" placement strategy or linestring features.
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
Whether to show the diagram.
QgsFields mFields
Layer&#39;s fields.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:196
const QgsMapToPixel & mapToPixel() const
The QgsAbstractLabelProvider class is an interface class.
double zIndex() const
Returns the diagram z-index.
QgsCoordinateReferenceSystem mLayerCrs
Layer&#39;s CRS.
double x
Definition: qgspointxy.h:47
void setFixedPosition(const QgsPointXY &point)
Sets coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
Partial snapshot of vector layer&#39;s state (only the members necessary for access to features) ...
void setRenderer(QgsDiagramRenderer *diagramRenderer)
Sets the diagram renderer associated with the layer.
virtual void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &featureRequest) const =0
Add additional filters to the feature request to further restrict the features returned by the reques...
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsExpressionContext & expressionContext()
Gets the expression context.
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
Stores the settings for rendering of all diagrams for a layer.
Class that adds extra information to QgsLabelFeature for labeling of diagrams.
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...
Placement
Placement modes which determine how label candidates are generated for a feature. ...
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:188
Placement placement() const
Returns the diagram placement.
double distance() const
Returns the distance between the diagram and the feature (in mm).
Contains information about the context of a rendering operation.
X-coordinate data defined diagram position.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition: qgsgeos.cpp:166
Transform from destination to source CRS.
QString mName
Name of the layer.
Diagram priority (between 0 and 10)
virtual void registerFeature(QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry=QgsGeometry())
Register a feature for labeling as one or more QgsLabelFeature objects stored into mFeatures...
double getWidth() const
double getX(int i=0) const
Returns the down-left x coordinate.
void setHasFixedAngle(bool enabled)
Sets whether the label should use a fixed angle instead of using angle from automatic placement...
QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context) override
Returns list of label features (they are owned by the provider and thus deleted on its destruction) ...
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
void setAttributes(const QgsAttributes &attrs)
Store feature&#39;s attributes - used for rendering of diagrams.
const QgsMapSettings & mapSettings() const
Gets associated map settings.
QgsLabelingResults * results() const
For internal use by the providers.
Class for doing transforms between two map coordinate systems.
LabelPosition is a candidate feature label position.
Definition: labelposition.h:55
void setObstacleGeometry(geos::unique_ptr obstacleGeom)
Sets the label&#39;s obstacle geometry, if different to the feature geometry.
Whether the diagram should always be shown, even if it overlaps other diagrams/labels.
virtual QSizeF sizeMapUnits(const QgsFeature &feature, const QgsRenderContext &c) const
Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error...
QgsGeometry geometry
Definition: qgsfeature.h:67
const QgsAttributes & attributes()
Gets feature&#39;s attributes - used for rendering of diagrams.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
QgsAbstractFeatureSource * mSource
Layer&#39;s feature source.
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
QString mLayerId
Associated layer&#39;s ID, if applicable.
QgsDiagramLayerSettings mSettings
Diagram layer settings.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
QgsAttributes attributes
Definition: qgsfeature.h:65
bool isActive() const
Returns whether the property is currently active.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
bool showAllDiagrams() const
Returns whether the layer should show all diagrams, including overlapping diagrams.
void setPriority(double priority)
Sets the priority for labeling the feature.