QGIS API Documentation  2.14.0-Essen
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 "qgslabelsearchtree.h"
19 #include "qgsvectorlayer.h"
21 #include "diagram/qgsdiagram.h"
22 
23 #include "feature.h"
24 #include "labelposition.h"
25 
26 
28  const QgsDiagramLayerSettings* diagSettings,
29  const QgsDiagramRendererV2* diagRenderer,
30  const QString& layerId,
31  const QgsFields& fields,
34  bool ownsSource )
35  : QgsAbstractLabelProvider( layerId )
36  , mSettings( *diagSettings )
37  , mDiagRenderer( diagRenderer->clone() )
38  , mLayerId( layerId )
39  , mFields( fields )
40  , mLayerCrs( crs )
41  , mSource( source )
42  , mOwnsSource( ownsSource )
43 {
44  init();
45 }
46 
47 
49  : QgsAbstractLabelProvider( layer->id() )
50  , mSettings( *layer->diagramLayerSettings() )
51  , mDiagRenderer( layer->diagramRenderer()->clone() )
52  , mLayerId( layer->id() )
53  , mFields( layer->fields() )
54  , mLayerCrs( layer->crs() )
55  , mSource( ownFeatureLoop ? new QgsVectorLayerFeatureSource( layer ) : nullptr )
56  , mOwnsSource( ownFeatureLoop )
57 {
58  init();
59 }
60 
61 
63 {
64  mName = mLayerId;
65  mPriority = 1 - mSettings.priority / 10.0; // convert 0..10 --> 1..0
68 }
69 
70 
72 {
73  if ( mOwnsSource )
74  delete mSource;
75 
76  qDeleteAll( mFeatures );
77 
78  // renderer is owned by mSettings
79 }
80 
81 
83 {
84  if ( !mSource )
85  {
86  // we have created the provider with "own feature loop" == false
87  // so it is assumed that prepare() has been already called followed by registerFeature() calls
88  return mFeatures;
89  }
90 
91  QStringList attributeNames;
92  if ( !prepare( context, attributeNames ) )
93  return QList<QgsLabelFeature*>();
94 
95  QgsRectangle layerExtent = context.extent();
96  if ( mSettings.ct )
98 
99  QgsFeatureRequest request;
100  request.setFilterRect( layerExtent );
101  request.setSubsetOfAttributes( attributeNames, mFields );
102  QgsFeatureIterator fit = mSource->getFeatures( request );
103 
104 
105  QgsFeature fet;
106  while ( fit.nextFeature( fet ) )
107  {
108  registerFeature( fet, context );
109  }
110 
111  return mFeatures;
112 }
113 
114 
116 {
117 #if 1 // XXX strk
118  // features are pre-rotated but not scaled/translated,
119  // so we only disable rotation here. Ideally, they'd be
120  // also pre-scaled/translated, as suggested here:
121  // http://hub.qgis.org/issues/11856
122  QgsMapToPixel xform = context.mapToPixel();
123  xform.setMapRotation( 0, 0, 0 );
124 #else
125  const QgsMapToPixel& xform = context.mapToPixel();
126 #endif
127 
128  QgsDiagramLabelFeature* dlf = dynamic_cast<QgsDiagramLabelFeature*>( label->getFeaturePart()->feature() );
129 
130  QgsFeature feature;
131  feature.setFields( mSettings.fields );
132  feature.setValid( true );
133  feature.setFeatureId( label->getFeaturePart()->featureId() );
134  feature.setAttributes( dlf->attributes() );
135 
136  //calculate top-left point for diagram
137  //first, calculate the centroid of the label (accounts for PAL creating
138  //rotated labels when we do not want to draw the diagrams rotated)
139  double centerX = 0;
140  double centerY = 0;
141  for ( int i = 0; i < 4; ++i )
142  {
143  centerX += label->getX( i );
144  centerY += label->getY( i );
145  }
146  QgsPoint outPt( centerX / 4.0, centerY / 4.0 );
147  //then, calculate the top left point for the diagram with this center position
148  QgsPoint centerPt = xform.transform( outPt.x() - label->getWidth() / 2,
149  outPt.y() - label->getHeight() / 2 );
150 
151  mSettings.renderer->renderDiagram( feature, context, centerPt.toQPointF() );
152 
153  //insert into label search tree to manipulate position interactively
154  mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false );
155 
156 }
157 
158 
160 {
162  const QgsMapSettings& mapSettings = mEngine->mapSettings();
163 
164  s2.ct = nullptr;
165  if ( mapSettings.hasCrsTransformEnabled() )
166  {
167  if ( context.coordinateTransform() )
168  // this is context for layer rendering - use its CT as it includes correct datum transform
169  s2.ct = context.coordinateTransform()->clone();
170  else
171  // otherwise fall back to creating our own CT - this one may not have the correct datum transform!
172  s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() );
173  }
174 
175  s2.xform = &mapSettings.mapToPixel();
176 
177  s2.fields = mFields;
178 
179  s2.renderer = mDiagRenderer;
180 
181  const QgsDiagramRendererV2* diagRenderer = s2.renderer;
182 
183  //add attributes needed by the diagram renderer
184  QList<QString> att = diagRenderer->diagramAttributes();
186  for ( ; attIt != att.constEnd(); ++attIt )
187  {
188  QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() );
189  QStringList columns = expression->referencedColumns();
190  QStringList::const_iterator columnsIterator = columns.constBegin();
191  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
192  {
193  if ( !attributeNames.contains( *columnsIterator ) )
194  attributeNames << *columnsIterator;
195  }
196  }
197 
198  const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( diagRenderer );
199  if ( linearlyInterpolatedDiagramRenderer )
200  {
201  if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() )
202  {
203  QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() );
204  QStringList columns = expression->referencedColumns();
205  QStringList::const_iterator columnsIterator = columns.constBegin();
206  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
207  {
208  if ( !attributeNames.contains( *columnsIterator ) )
209  attributeNames << *columnsIterator;
210  }
211  }
212  else
213  {
214  QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name();
215  if ( !attributeNames.contains( name ) )
216  attributeNames << name;
217  }
218  }
219 
220  //and the ones needed for data defined diagram positions
221  if ( mSettings.xPosColumn != -1 )
222  attributeNames << mFields.at( mSettings.xPosColumn ).name();
223  if ( mSettings.yPosColumn != -1 )
224  attributeNames << mFields.at( mSettings.yPosColumn ).name();
225 
226  return true;
227 }
228 
229 
231 {
232  QgsLabelFeature* label = registerDiagram( feature, context, obstacleGeometry );
233  if ( label )
234  mFeatures << label;
235 }
236 
237 
239 {
240  const QgsMapSettings& mapSettings = mEngine->mapSettings();
241 
243  if ( dr )
244  {
245  QList<QgsDiagramSettings> settingList = dr->diagramSettings();
246  if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility )
247  {
248  double minScale = settingList.at( 0 ).minScaleDenominator;
249  if ( minScale > 0 && context.rendererScale() < minScale )
250  {
251  return nullptr;
252  }
253 
254  double maxScale = settingList.at( 0 ).maxScaleDenominator;
255  if ( maxScale > 0 && context.rendererScale() > maxScale )
256  {
257  return nullptr;
258  }
259  }
260  }
261 
262  //convert geom to geos
263  const QgsGeometry* geom = feat.constGeometry();
264  QScopedPointer<QgsGeometry> extentGeom( QgsGeometry::fromRect( mapSettings.visibleExtent() ) );
265  if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
266  {
267  //PAL features are prerotated, so extent also needs to be unrotated
268  extentGeom->rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
269  }
270 
271  const GEOSGeometry* geos_geom = nullptr;
272  QScopedPointer<QgsGeometry> preparedGeom;
273  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.ct, extentGeom.data() ) )
274  {
275  preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, mSettings.ct, extentGeom.data() ) );
276  if ( !preparedGeom.data() )
277  return nullptr;
278  geos_geom = preparedGeom.data()->asGeos();
279  }
280  else
281  {
282  geos_geom = geom->asGeos();
283  }
284 
285  if ( !geos_geom )
286  return nullptr; // invalid geometry
287 
288  GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
289 
290  const GEOSGeometry* geosObstacleGeom = nullptr;
291  QScopedPointer<QgsGeometry> scopedObstacleGeom;
292  if ( mSettings.obstacle && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, mSettings.ct, extentGeom.data() ) )
293  {
294  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, mSettings.ct, extentGeom.data() ) );
295  geosObstacleGeom = scopedObstacleGeom.data()->asGeos();
296  }
297  else if ( mSettings.obstacle && obstacleGeometry )
298  {
299  geosObstacleGeom = obstacleGeometry->asGeos();
300  }
301  GEOSGeometry* geosObstacleGeomClone = nullptr;
302  if ( geosObstacleGeom )
303  {
304  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
305  }
306 
307 
308  double diagramWidth = 0;
309  double diagramHeight = 0;
310  if ( dr )
311  {
312  QSizeF diagSize = dr->sizeMapUnits( feat, context );
313  if ( diagSize.isValid() )
314  {
315  diagramWidth = diagSize.width();
316  diagramHeight = diagSize.height();
317  }
318  }
319 
320  // feature to the layer
321  bool alwaysShow = mSettings.showAll;
322  int ddColX = mSettings.xPosColumn;
323  int ddColY = mSettings.yPosColumn;
324  double ddPosX = 0.0;
325  double ddPosY = 0.0;
326  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
327  if ( ddPos )
328  {
329  bool posXOk, posYOk;
330  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk );
331  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk );
332  if ( !posXOk || !posYOk )
333  {
334  ddPos = false;
335  }
336  else
337  {
338  const QgsCoordinateTransform* ct = mSettings.ct;
339  if ( ct )
340  {
341  double z = 0;
342  ct->transformInPlace( ddPosX, ddPosY, z );
343  }
344  //data defined diagram position is always centered
345  ddPosX -= diagramWidth / 2.0;
346  ddPosY -= diagramHeight / 2.0;
347  }
348  }
349 
350  QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) );
351  lf->setHasFixedPosition( ddPos );
352  lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) );
353  lf->setHasFixedAngle( true );
354  lf->setFixedAngle( 0 );
355  lf->setAlwaysShow( alwaysShow );
357  lf->setZIndex( mSettings.zIndex );
358  if ( geosObstacleGeomClone )
359  {
360  lf->setObstacleGeometry( geosObstacleGeomClone );
361  }
362 
363  if ( dr )
364  {
365  //append the diagram attributes to lbl
366  lf->setAttributes( feat.attributes() );
367  }
368 
369  QgsPoint ptZero = mSettings.xform->toMapCoordinates( 0, 0 );
370  QgsPoint ptOne = mSettings.xform->toMapCoordinates( 1, 0 );
371  lf->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * mSettings.dist );
372  return lf;
373 }
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
double zIndex
Z-index of diagrams, where diagrams with a higher z-index are drawn on top of diagrams with a lower z...
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsLabelFeature * registerDiagram(QgsFeature &feat, QgsRenderContext &context, QgsGeometry *obstacleGeometry=nullptr)
helper method to register one diagram feautre
Wrapper for iterator of features from vector data provider or vector layer.
QString name() const
Name of the layer (for statistics, debugging etc.) - does not need to be unique.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static QgsGeometry * prepareGeometry(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=nullptr)
Prepares a geometry for registration with PAL.
void setMapRotation(double degrees, double cx, double cy)
Set map rotation in degrees (clockwise)
QStringList referencedColumns() const
Get list of columns referenced by the expression.
QgsPalLayerSettings::Placement mPlacement
Placement strategy.
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames)
Prepare for registration of features.
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:109
double getWidth() const
virtual QList< QString > diagramAttributes() const =0
Returns attribute indices needed for diagram rendering.
Q_DECL_DEPRECATED QgsExpression * getExpression(const QString &expression, const QgsFields *fields)
Definition: qgsdiagram.cpp:47
double mPriority
Default priority of labels.
void init()
initialization method - called from constructors
QgsDiagram * diagram() const
double rendererScale() const
QgsPoint transform(const QgsPoint &p) const
Transform the point from map (world) coordinates to device coordinates.
const T & at(int i) const
bool isValid() const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
QgsVectorLayerDiagramProvider(QgsVectorLayer *layer, bool ownFeatureLoop=true)
Convenience constructor to initialize the provider from given vector layer.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
Container of fields for a vector layer.
Definition: qgsfield.h:187
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:115
const QgsRectangle & extent() const
const QgsMapToPixel & mapToPixel() const
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, QPointF pos)
QgsDiagramRendererV2 * mDiagRenderer
Diagram renderer instance (owned by mSettings)
double rotation() const
Return the rotation of the resulting map image Units are clockwise degrees.
FeaturePart * getFeaturePart()
return 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:187
void setFixedPosition(const QgsPoint &point)
Set coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
static bool geometryRequiresPreparation(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=nullptr)
Checks whether a geometry requires preparation before registration with PAL.
const QgsCoordinateTransform * coordinateTransform() const
void setAlwaysShow(bool enabled)
Set whether label should be always shown (sets very high label priority)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
Returns diagram settings for a feature.
void setHasFixedPosition(bool enabled)
Set whether the label should use a fixed position instead of being automatically placed.
void reset(T *other)
The QgsMapSettings class contains configuration for rendering of the map.
QgsLabelingResults * results() const
For internal use by the providers.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
const QgsMapToPixel * xform
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.
void setFeatureId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:101
virtual QSizeF sizeMapUnits(const QgsFeature &feature, const QgsRenderContext &c)
Returns size of the diagram for a feature in map units.
unsigned int mLinePlacementFlags
Extra placement flags for linestring geometries.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
const QgsMapSettings & mapSettings() const
Get associated map settings.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
QString name() const
Gets the name of the field.
Definition: qgsfield.cpp:84
bool isEmpty() const
double getY(int i=0) const
get the down-left y coordinate
void setFixedAngle(double angle)
Set 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)...
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const override
draw this label at the position determined by the labeling engine
const QgsLabelingEngineV2 * mEngine
Associated labeling engine.
QgsDiagramRendererV2 * renderer
void setDistLabel(double dist)
Applies to "around point" placement strategy or linestring features.
const GEOSGeometry * asGeos(double precision=0) const
Returns a geos geometry.
Base class that can be used for any class that is capable of returning features.
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
double getX(int i=0) const
get the down-left x coordinate
QgsFields mFields
Layer&#39;s fields.
A class to represent a point.
Definition: qgspoint.h:65
Q_DECL_DEPRECATED 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:173
The QgsAbstractLabelProvider class is an interface class.
virtual void registerFeature(QgsFeature &feature, QgsRenderContext &context, QgsGeometry *obstacleGeometry=nullptr)
Register a feature for labeling as one or more QgsLabelFeature objects stored into mFeatures...
QgsCoordinateReferenceSystem mLayerCrs
Layer&#39;s CRS.
T * data() const
Partial snapshot of vector layer&#39;s state (only the members necessary for access to features) ...
QgsExpressionContext & expressionContext()
Gets the expression context.
Class that adds extra information to QgsLabelFeature for labeling of diagrams.
QgsPoint toMapCoordinates(int x, int y) const
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:204
Contains information about the context of a rendering operation.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
static GEOSContextHandle_t getGEOSHandler()
Return GEOS context handle.
QString mName
Name of the layer.
double getHeight() const
void setHasFixedAngle(bool enabled)
Set whether the label should use a fixed angle instead of using angle from automatic placement...
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context) override
Return list of label features (they are owned by the provider and thus deleted on its destruction) ...
void setAttributes(const QgsAttributes &attrs)
Store feature&#39;s attributes - used for rendering of diagrams.
void setObstacleGeometry(GEOSGeometry *obstacleGeom)
Sets the label&#39;s obstacle geometry, if different to the feature geometry.
Class for storing a coordinate reference system (CRS)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
Class for doing transforms between two map coordinate systems.
static QgsGeometry * fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
LabelPosition is a candidate feature label position.
Definition: labelposition.h:50
const QgsMapToPixel & mapToPixel() const
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
double toDouble(bool *ok) const
const QgsAttributes & attributes()
Get feature&#39;s attributes - used for rendering of diagrams.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request)=0
Get an iterator for features matching the specified request.
const_iterator constEnd() const
QgsAbstractFeatureSource * mSource
Layer&#39;s feature source.
bool nextFeature(QgsFeature &f)
const_iterator constBegin() const
qreal height() const
bool insertLabel(pal::LabelPosition *labelPos, int featureId, const QString &layerName, const QString &labeltext, const QFont &labelfont, bool diagram=false, bool pinned=false)
Inserts label position.
Represents a vector layer which manages a vector based data sets.
QgsDiagramLayerSettings mSettings
Diagram layer settings.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:217
const QgsCoordinateTransform * ct
QList< QgsLabelFeature * > mFeatures
List of generated label features (owned by the provider)
qreal width() const
QgsFeatureRequest & setFilterRect(const QgsRectangle &rect)
Set rectangle from which features will be taken.
QgsRectangle transformBoundingBox(const QgsRectangle &theRect, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transform a QgsRectangle to the dest Coordinate system If the direction is ForwardTransform then coor...
QgsCoordinateTransform * clone() const
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:154
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspoint.cpp:121