QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 "qgsrendercontext.h"
20#include "qgsrenderer.h"
21#include "qgsvectorlayer.h"
22#include "qgssymbollayerutils.h"
23#include "qgsgeometry.h"
24#include "qgsgeometryengine.h"
26#include "qgsmaplayerstyle.h"
27
28QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const QgsGeometry &polygon, const LayerFilterExpression &layerFilterExpression )
29 : mSettings( settings )
30 , mLayerFilterExpression( layerFilterExpression )
31 , mOnlyExpressions( false )
32{
33 if ( !polygon.isNull() && polygon.type() == Qgis::GeometryType::Polygon )
34 {
35 mPolygon = polygon;
36 }
37}
38
39QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const LayerFilterExpression &layerFilterExpression )
40 : mSettings( settings )
41 , mLayerFilterExpression( layerFilterExpression )
42 , mOnlyExpressions( true )
43{
44}
45
47{
48 // TODO: do we need this temp image?
49 QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() );
50 tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 );
51 tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 );
52 QPainter painter( &tmpImage );
53
55 context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter
56
57 const QList< QgsMapLayer * > layers = mSettings.layers( true );
58 for ( QgsMapLayer *layer : 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
94bool 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
102void 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 const bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature;
110 r->startRender( context, vl->fields() );
111
112 QgsFeatureRequest request;
113
114 const QString rendererFilterExpression = r->filter( vl->fields() );
115 if ( !rendererFilterExpression.isEmpty() )
116 {
117 request.setFilterExpression( rendererFilterExpression );
118 }
119
120 QSet<QString> requiredAttributes = r->usedAttributes( context );
121
122 QgsGeometry transformedPolygon = mPolygon;
123 if ( !mOnlyExpressions && !mPolygon.isNull() )
124 {
125 if ( mSettings.destinationCrs() != vl->crs() )
126 {
127 const QgsCoordinateTransform ct( mSettings.destinationCrs(), vl->crs(), mSettings.transformContext() );
128 transformedPolygon.transform( ct );
129 }
130 }
131
132 std::unique_ptr<QgsExpression> expr;
133 if ( mLayerFilterExpression.contains( vl->id() ) )
134 {
135 const QString expression = mLayerFilterExpression[vl->id()];
136 expr.reset( new QgsExpression( expression ) );
137 expr->prepare( &context.expressionContext() );
138
139 requiredAttributes.unite( expr->referencedColumns() );
140 request.combineFilterExpression( expression );
141 }
142
143 request.setSubsetOfAttributes( requiredAttributes, vl->fields() );
144
145 std::unique_ptr< QgsGeometryEngine > polygonEngine;
146 if ( !mOnlyExpressions )
147 {
148 if ( mPolygon.isNull() )
149 {
150 request.setFilterRect( context.extent() );
152 }
153 else
154 {
155 request.setFilterRect( transformedPolygon.boundingBox() );
156 polygonEngine.reset( QgsGeometry::createGeometryEngine( transformedPolygon.constGet() ) );
157 polygonEngine->prepareGeometry();
158 }
159 }
160 QgsFeatureIterator fi = vl->getFeatures( request );
161
162 usedSymbols.clear();
163 usedSymbolsRuleKey.clear();
164
165 QgsFeature f;
166 while ( fi.nextFeature( f ) )
167 {
168 // filter out elements outside of the polygon
169 if ( f.hasGeometry() && polygonEngine )
170 {
171 if ( !polygonEngine->intersects( f.geometry().constGet() ) )
172 {
173 continue;
174 }
175 }
176
177 context.expressionContext().setFeature( f );
178
179 //make sure we store string representation of symbol, not pointer
180 //otherwise layer style override changes will delete original symbols and leave hanging pointers
181 const auto constLegendKeysForFeature = r->legendKeysForFeature( f, context );
182 for ( const QString &legendKey : constLegendKeysForFeature )
183 {
184 usedSymbolsRuleKey.insert( legendKey );
185 }
186
187 if ( moreSymbolsPerFeature )
188 {
189 const auto constOriginalSymbolsForFeature = r->originalSymbolsForFeature( f, context );
190 for ( QgsSymbol *s : constOriginalSymbolsForFeature )
191 {
192 if ( s )
193 usedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) );
194 }
195 }
196 else
197 {
198 QgsSymbol *s = r->originalSymbolForFeature( f, context );
199 if ( s )
200 usedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) );
201 }
202 }
203 r->stopRender( context );
204}
205
Class for doing transforms between two map coordinate systems.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
@ MoreSymbolsPerFeature
May use more than one symbol to render a feature: symbolsForFeature() will return them.
Definition: qgsrenderer.h:274
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & combineFilterExpression(const QString &expression)
Modifies the existing filter expression to add an additional expression filter.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
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
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
Qgis::GeometryType type
Definition: qgsgeometry.h:167
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
void run()
Runs the map hit test.
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.
QgsMapHitTest(const QgsMapSettings &settings, const QgsGeometry &polygon=QgsGeometry(), const QgsMapHitTest::LayerFilterExpression &layerFilterExpression=QgsMapHitTest::LayerFilterExpression())
QMap< QString, QString > LayerFilterExpression
Maps an expression string to a layer id.
Definition: qgsmaphittest.h:41
Restore overridden layer style on destruction.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
double scale() const
Returns the calculated map scale.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsRectangle outputExtentToLayerExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from output CRS to layer's CRS
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Contains information about the context of a rendering operation.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:873
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.