QGIS API Documentation  2.12.0-Lyon
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  : mSettings( *diagSettings )
36  , mDiagRenderer( diagRenderer->clone() )
37  , mLayerId( layerId )
38  , mFields( fields )
39  , mLayerCrs( crs )
40  , mSource( source )
41  , mOwnsSource( ownsSource )
42 {
43  init();
44 }
45 
46 
48  : mSettings( *layer->diagramLayerSettings() )
49  , mDiagRenderer( layer->diagramRenderer()->clone() )
50  , mLayerId( layer->id() )
51  , mFields( layer->fields() )
52  , mLayerCrs( layer->crs() )
53  , mSource( ownFeatureLoop ? new QgsVectorLayerFeatureSource( layer ) : 0 )
54  , mOwnsSource( ownFeatureLoop )
55 {
56  init();
57 }
58 
59 
61 {
62  mName = mLayerId;
63  mPriority = 1 - mSettings.priority / 10.0; // convert 0..10 --> 1..0
66 }
67 
68 
70 {
71  if ( mOwnsSource )
72  delete mSource;
73 
74  qDeleteAll( mFeatures );
75 
76  // renderer is owned by mSettings
77 }
78 
79 
81 {
82  if ( !mSource )
83  {
84  // we have created the provider with "own feature loop" == false
85  // so it is assumed that prepare() has been already called followed by registerFeature() calls
86  return mFeatures;
87  }
88 
89  QStringList attributeNames;
90  if ( !prepare( context, attributeNames ) )
91  return QList<QgsLabelFeature*>();
92 
93  QgsRectangle layerExtent = context.extent();
94  if ( mSettings.ct )
96 
97  QgsFeatureRequest request;
98  request.setFilterRect( layerExtent );
99  request.setSubsetOfAttributes( attributeNames, mFields );
100  QgsFeatureIterator fit = mSource->getFeatures( request );
101 
102 
103  QgsFeature fet;
104  while ( fit.nextFeature( fet ) )
105  {
106  registerFeature( fet, context );
107  }
108 
109  return mFeatures;
110 }
111 
112 
114 {
115 #if 1 // XXX strk
116  // features are pre-rotated but not scaled/translated,
117  // so we only disable rotation here. Ideally, they'd be
118  // also pre-scaled/translated, as suggested here:
119  // http://hub.qgis.org/issues/11856
120  QgsMapToPixel xform = context.mapToPixel();
121  xform.setMapRotation( 0, 0, 0 );
122 #else
123  const QgsMapToPixel& xform = context.mapToPixel();
124 #endif
125 
126  QgsDiagramLabelFeature* dlf = dynamic_cast<QgsDiagramLabelFeature*>( label->getFeaturePart()->feature() );
127 
128  QgsFeature feature;
129  feature.setFields( mSettings.fields );
130  feature.setValid( true );
131  feature.setFeatureId( label->getFeaturePart()->featureId() );
132  feature.setAttributes( dlf->attributes() );
133 
134  //calculate top-left point for diagram
135  //first, calculate the centroid of the label (accounts for PAL creating
136  //rotated labels when we do not want to draw the diagrams rotated)
137  double centerX = 0;
138  double centerY = 0;
139  for ( int i = 0; i < 4; ++i )
140  {
141  centerX += label->getX( i );
142  centerY += label->getY( i );
143  }
144  QgsPoint outPt( centerX / 4.0, centerY / 4.0 );
145  //then, calculate the top left point for the diagram with this center position
146  QgsPoint centerPt = xform.transform( outPt.x() - label->getWidth() / 2,
147  outPt.y() - label->getHeight() / 2 );
148 
149  mSettings.renderer->renderDiagram( feature, context, centerPt.toQPointF() );
150 
151  //insert into label search tree to manipulate position interactively
152  mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false );
153 
154 }
155 
156 
158 {
160  const QgsMapSettings& mapSettings = mEngine->mapSettings();
161 
162  s2.ct = 0;
163  if ( mapSettings.hasCrsTransformEnabled() )
164  {
165  if ( context.coordinateTransform() )
166  // this is context for layer rendering - use its CT as it includes correct datum transform
167  s2.ct = context.coordinateTransform()->clone();
168  else
169  // otherwise fall back to creating our own CT - this one may not have the correct datum transform!
170  s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() );
171  }
172 
173  s2.xform = &mapSettings.mapToPixel();
174 
175  s2.fields = mFields;
176 
177  s2.renderer = mDiagRenderer;
178 
179  const QgsDiagramRendererV2* diagRenderer = s2.renderer;
180 
181  //add attributes needed by the diagram renderer
182  QList<QString> att = diagRenderer->diagramAttributes();
184  for ( ; attIt != att.constEnd(); ++attIt )
185  {
186  QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() );
187  QStringList columns = expression->referencedColumns();
188  QStringList::const_iterator columnsIterator = columns.constBegin();
189  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
190  {
191  if ( !attributeNames.contains( *columnsIterator ) )
192  attributeNames << *columnsIterator;
193  }
194  }
195 
196  const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( diagRenderer );
197  if ( linearlyInterpolatedDiagramRenderer != NULL )
198  {
199  if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() )
200  {
201  QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() );
202  QStringList columns = expression->referencedColumns();
203  QStringList::const_iterator columnsIterator = columns.constBegin();
204  for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
205  {
206  if ( !attributeNames.contains( *columnsIterator ) )
207  attributeNames << *columnsIterator;
208  }
209  }
210  else
211  {
212  QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name();
213  if ( !attributeNames.contains( name ) )
214  attributeNames << name;
215  }
216  }
217 
218  //and the ones needed for data defined diagram positions
219  if ( mSettings.xPosColumn != -1 )
220  attributeNames << mFields.at( mSettings.xPosColumn ).name();
221  if ( mSettings.yPosColumn != -1 )
222  attributeNames << mFields.at( mSettings.yPosColumn ).name();
223 
224  return true;
225 }
226 
227 
229 {
230  QgsLabelFeature* label = registerDiagram( feature, context );
231  if ( label )
232  mFeatures << label;
233 }
234 
235 
237 {
238  const QgsMapSettings& mapSettings = mEngine->mapSettings();
239 
241  if ( dr )
242  {
243  QList<QgsDiagramSettings> settingList = dr->diagramSettings();
244  if ( settingList.size() > 0 && settingList.at( 0 ).scaleBasedVisibility )
245  {
246  double minScale = settingList.at( 0 ).minScaleDenominator;
247  if ( minScale > 0 && context.rendererScale() < minScale )
248  {
249  return 0;
250  }
251 
252  double maxScale = settingList.at( 0 ).maxScaleDenominator;
253  if ( maxScale > 0 && context.rendererScale() > maxScale )
254  {
255  return 0;
256  }
257  }
258  }
259 
260  //convert geom to geos
261  const QgsGeometry* geom = feat.constGeometry();
262  QScopedPointer<QgsGeometry> extentGeom( QgsGeometry::fromRect( mapSettings.visibleExtent() ) );
263  if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
264  {
265  //PAL features are prerotated, so extent also needs to be unrotated
266  extentGeom->rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
267  }
268 
269  const GEOSGeometry* geos_geom = 0;
270  QScopedPointer<QgsGeometry> preparedGeom;
271  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.ct, extentGeom.data() ) )
272  {
273  preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, mSettings.ct, extentGeom.data() ) );
274  if ( !preparedGeom.data() )
275  return 0;
276  geos_geom = preparedGeom.data()->asGeos();
277  }
278  else
279  {
280  geos_geom = geom->asGeos();
281  }
282 
283  if ( geos_geom == 0 )
284  {
285  return 0; // invalid geometry
286  }
287 
288  GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
289 
290  double diagramWidth = 0;
291  double diagramHeight = 0;
292  if ( dr )
293  {
294  QSizeF diagSize = dr->sizeMapUnits( feat, context );
295  if ( diagSize.isValid() )
296  {
297  diagramWidth = diagSize.width();
298  diagramHeight = diagSize.height();
299  }
300  }
301 
302  // feature to the layer
303  bool alwaysShow = mSettings.showAll;
304  int ddColX = mSettings.xPosColumn;
305  int ddColY = mSettings.yPosColumn;
306  double ddPosX = 0.0;
307  double ddPosY = 0.0;
308  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
309  if ( ddPos )
310  {
311  bool posXOk, posYOk;
312  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk );
313  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk );
314  if ( !posXOk || !posYOk )
315  {
316  ddPos = false;
317  }
318  else
319  {
320  const QgsCoordinateTransform* ct = mSettings.ct;
321  if ( ct )
322  {
323  double z = 0;
324  ct->transformInPlace( ddPosX, ddPosY, z );
325  }
326  //data defined diagram position is always centered
327  ddPosX -= diagramWidth / 2.0;
328  ddPosY -= diagramHeight / 2.0;
329  }
330  }
331 
332  QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) );
333  lf->setHasFixedPosition( ddPos );
334  lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) );
335  lf->setHasFixedAngle( true );
336  lf->setFixedAngle( 0 );
337  lf->setAlwaysShow( alwaysShow );
339 
340  if ( dr )
341  {
342  //append the diagram attributes to lbl
343  lf->setAttributes( feat.attributes() );
344  }
345 
346  QgsPoint ptZero = mSettings.xform->toMapCoordinates( 0, 0 );
347  QgsPoint ptOne = mSettings.xform->toMapCoordinates( 1, 0 );
348  lf->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * mSettings.dist );
349  return lf;
350 }
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:53
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:72
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.
FeaturePart * getFeaturePart()
return the feature corresponding to this labelposition
A rectangle specified with double values.
Definition: qgsrectangle.h:35
virtual void registerFeature(QgsFeature &feature, QgsRenderContext &context)
Register a feature for labeling as one or more QgsLabelFeature objects stored into mFeatures...
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:140
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:177
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:97
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:152
const QgsRectangle & extent() const
const QgsMapToPixel & mapToPixel() const
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, const 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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:176
void setFixedPosition(const QgsPoint &point)
Set coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
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:268
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
int size() const
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
static bool geometryRequiresPreparation(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=0)
Checks whether a geometry requires preparation before registration with PAL.
bool mOwnsSource
Whether layer's feature source is owned.
void setIsObstacle(bool enabled)
Sets whether the feature will act as an obstacle for labels.
void setFeatureId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:83
double getY(int i=0) const
get the down-left y coordinate
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's attributes.
Definition: qgsfeature.cpp:92
const QgsMapSettings & mapSettings() const
Get associated map settings.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
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:331
A class to represent a point.
Definition: qgspoint.h:63
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:149
QgsCoordinateReferenceSystem mLayerCrs
Layer's CRS.
bool insertLabel(LabelPosition *labelPos, int featureId, const QString &layerName, const QString &labeltext, const QFont &labelfont, bool diagram=false, bool pinned=false)
Inserts label position.
T * data() const
Partial snapshot of vector layer'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
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:175
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:238
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's attributes - used for rendering of diagrams.
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:70
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:48
const QgsMapToPixel & mapToPixel() const
static QgsGeometry * prepareGeometry(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=0)
Prepares a geometry for registration with PAL.
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
double toDouble(bool *ok) const
double getX(int i=0) const
get the down-left x coordinate
const QgsAttributes & attributes()
Get feature'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's feature source.
bool nextFeature(QgsFeature &f)
const_iterator constBegin() const
qreal height() const
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:216
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.
QgsLabelFeature * registerDiagram(QgsFeature &feat, QgsRenderContext &context)
helper method to register one diagram feautre
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
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspoint.cpp:121