QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsmaphittest.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaphittest.cpp
3  ---------------------
4  begin : September 2014
5  copyright : (C) 2014 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 
16 #include "qgsmaphittest.h"
17 
18 #include "qgsfeatureiterator.h"
19 #include "qgsproject.h"
20 #include "qgsrendercontext.h"
22 #include "qgsrenderer.h"
24 #include "qgsvectorlayer.h"
25 #include "qgssymbollayerutils.h"
26 #include "qgsgeometry.h"
27 #include "qgsgeometryengine.h"
28 
29 QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const QgsGeometry &polygon, const LayerFilterExpression &layerFilterExpression )
30  : mSettings( settings )
31  , mLayerFilterExpression( layerFilterExpression )
32  , mOnlyExpressions( false )
33 {
34  if ( !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry )
35  {
36  mPolygon = polygon;
37  }
38 }
39 
40 QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const LayerFilterExpression &layerFilterExpression )
41  : mSettings( settings )
42  , mLayerFilterExpression( layerFilterExpression )
43  , mOnlyExpressions( true )
44 {
45 }
46 
48 {
49  // TODO: do we need this temp image?
50  QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() );
51  tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 );
52  tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 );
53  QPainter painter( &tmpImage );
54 
56  context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter
57 
58  Q_FOREACH ( QgsMapLayer *layer, mSettings.layers() )
59  {
60  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
61  if ( !vl || !vl->renderer() )
62  continue;
63 
64  if ( !mOnlyExpressions )
65  {
66  if ( !vl->isInScaleRange( mSettings.scale() ) )
67  {
68  mHitTest[vl] = SymbolSet(); // no symbols -> will not be shown
69  mHitTestRuleKey[vl] = SymbolSet();
70  continue;
71  }
72 
73  context.setCoordinateTransform( mSettings.layerTransform( vl ) );
74  context.setExtent( mSettings.outputExtentToLayerExtent( vl, mSettings.visibleExtent() ) );
75  }
76 
78  SymbolSet &usedSymbols = mHitTest[vl];
79  SymbolSet &usedSymbolsRuleKey = mHitTestRuleKey[vl];
80  runHitTestLayer( vl, usedSymbols, usedSymbolsRuleKey, context );
81  }
82 
83  painter.end();
84 }
85 
87 {
88  if ( !symbol || !layer || !mHitTest.contains( layer ) )
89  return false;
90 
91  return mHitTest.value( layer ).contains( QgsSymbolLayerUtils::symbolProperties( symbol ) );
92 }
93 
94 bool QgsMapHitTest::legendKeyVisible( const QString &ruleKey, QgsVectorLayer *layer ) const
95 {
96  if ( !layer || !mHitTestRuleKey.contains( layer ) )
97  return false;
98 
99  return mHitTestRuleKey.value( layer ).contains( ruleKey );
100 }
101 
102 void QgsMapHitTest::runHitTestLayer( QgsVectorLayer *vl, SymbolSet &usedSymbols, SymbolSet &usedSymbolsRuleKey, QgsRenderContext &context )
103 {
104  QgsMapLayerStyleOverride styleOverride( vl );
105  if ( mSettings.layerStyleOverrides().contains( vl->id() ) )
106  styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( vl->id() ) );
107 
108  std::unique_ptr< QgsFeatureRenderer > r( vl->renderer()->clone() );
109  bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature;
110  r->startRender( context, vl->fields() );
111 
112  QgsGeometry transformedPolygon = mPolygon;
113  if ( !mOnlyExpressions && !mPolygon.isNull() )
114  {
115  if ( mSettings.destinationCrs() != vl->crs() )
116  {
117  QgsCoordinateTransform ct( mSettings.destinationCrs(), vl->crs(), mSettings.transformContext() );
118  transformedPolygon.transform( ct );
119  }
120  }
121 
122  QgsFeature f;
123  QgsFeatureRequest request;
124  std::unique_ptr< QgsGeometryEngine > polygonEngine;
125  if ( !mOnlyExpressions )
126  {
127  if ( mPolygon.isNull() )
128  {
129  request.setFilterRect( context.extent() );
131  }
132  else
133  {
134  request.setFilterRect( transformedPolygon.boundingBox() );
135  polygonEngine.reset( QgsGeometry::createGeometryEngine( transformedPolygon.constGet() ) );
136  polygonEngine->prepareGeometry();
137  }
138  }
139  QgsFeatureIterator fi = vl->getFeatures( request );
140 
141  SymbolSet lUsedSymbols;
142  SymbolSet lUsedSymbolsRuleKey;
143  bool allExpressionFalse = false;
144  bool hasExpression = mLayerFilterExpression.contains( vl->id() );
145  std::unique_ptr<QgsExpression> expr;
146  if ( hasExpression )
147  {
148  expr.reset( new QgsExpression( mLayerFilterExpression[vl->id()] ) );
149  expr->prepare( &context.expressionContext() );
150  }
151  while ( fi.nextFeature( f ) )
152  {
153  context.expressionContext().setFeature( f );
154  // filter out elements outside of the polygon
155  if ( f.geometry() && polygonEngine )
156  {
157  if ( !polygonEngine->intersects( f.geometry().constGet() ) )
158  {
159  continue;
160  }
161  }
162 
163  // filter out elements where the expression is false
164  if ( hasExpression )
165  {
166  if ( !expr->evaluate( &context.expressionContext() ).toBool() )
167  continue;
168  else
169  allExpressionFalse = false;
170  }
171 
172  //make sure we store string representation of symbol, not pointer
173  //otherwise layer style override changes will delete original symbols and leave hanging pointers
174  Q_FOREACH ( const QString &legendKey, r->legendKeysForFeature( f, context ) )
175  {
176  lUsedSymbolsRuleKey.insert( legendKey );
177  }
178 
179  if ( moreSymbolsPerFeature )
180  {
181  Q_FOREACH ( QgsSymbol *s, r->originalSymbolsForFeature( f, context ) )
182  {
183  if ( s )
184  lUsedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) );
185  }
186  }
187  else
188  {
189  QgsSymbol *s = r->originalSymbolForFeature( f, context );
190  if ( s )
191  lUsedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) );
192  }
193  }
194  r->stopRender( context );
195 
196  if ( !allExpressionFalse )
197  {
198  // QSet is implicitly shared => constant time
199  usedSymbols = lUsedSymbols;
200  usedSymbolsRuleKey = lUsedSymbolsRuleKey;
201  }
202 }
203 
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
May use more than one symbol to render a feature: symbolsForFeature() will return them...
Definition: qgsrenderer.h:243
Base class for all map layer types.
Definition: qgsmaplayer.h:61
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Use exact geometry intersection (slower) instead of bounding boxes.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets coordinate transformation.
Restore overridden layer style on destruction.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsRectangle outputExtentToLayerExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from output CRS to layer&#39;s CRS
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
void setExtent(const QgsRectangle &extent)
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer&#39;s CRS to destination CRS.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QgsFields fields() const override
Returns the list of fields of this layer.
QgsMapHitTest(const QgsMapSettings &settings, const QgsGeometry &polygon=QgsGeometry(), const QgsMapHitTest::LayerFilterExpression &layerFilterExpression=QgsMapHitTest::LayerFilterExpression())
const QgsRectangle & extent() const
double scale() const
Returns the calculated map scale.
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsFeatureRenderer * renderer()
Returns renderer.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsExpressionContext & expressionContext()
Gets the expression context.
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
double outputDpi() const
Returns DPI used for conversion between real world units (e.g.
void run()
Runs the map hit test.
Contains information about the context of a rendering operation.
QMap< QString, QString > LayerFilterExpression
Maps an expression string to a layer id.
Definition: qgsmaphittest.h:41
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Class for doing transforms between two map coordinate systems.
bool symbolVisible(QgsSymbol *symbol, QgsVectorLayer *layer) const
Tests whether a symbol is visible for a specified layer.
bool legendKeyVisible(const QString &ruleKey, QgsVectorLayer *layer) const
Tests whether a given legend key is visible for a specified layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Represents a vector layer which manages a vector based data sets.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:418
QSize outputSize() const
Returns the size of the resulting map image.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.