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