QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsquickidentifykit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsquickidentifykit.cpp
3  ---------------------
4  Date : 30.8.2016
5  Copyright : (C) 2016 by Matthias Kuhn
6  Email : matthias (at) opengis.ch
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 "qgsmessagelog.h"
17 #include "qgsproject.h"
18 #include "qgslogger.h"
19 #include "qgsrenderer.h"
20 #include "qgsvectorlayer.h"
21 
22 #include "qgsquickidentifykit.h"
23 #include "qgsquickmapsettings.h"
25 
26 #include "qgis.h"
27 
29  : QObject( parent )
30 {
31 }
32 
34 {
35  return mMapSettings;
36 }
37 
39 {
40  if ( mapSettings == mMapSettings )
41  return;
42 
43  mMapSettings = mapSettings;
44  emit mapSettingsChanged();
45 }
46 
48 {
50 
51  if ( !mMapSettings )
52  {
53  QgsDebugMsg( QStringLiteral( "Unable to use IdentifyKit without mapSettings property set." ) );
54  return results;
55  }
56  QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() );
57 
58  if ( layer )
59  {
60  QgsFeatureList featureList = identifyVectorLayer( layer, mapPoint );
61  for ( const QgsFeature &feature : featureList )
62  {
63  results.append( QgsQuickFeatureLayerPair( feature, layer ) );
64  }
65  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) );
66  }
67  else
68  {
69  for ( QgsMapLayer *layer : mMapSettings->mapSettings().layers() )
70  {
71  if ( mMapSettings->project() && !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
72  continue;
73 
74  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
75  if ( vl )
76  {
77  QgsFeatureList featureList = identifyVectorLayer( vl, mapPoint );
78 
79  for ( const QgsFeature &feature : featureList )
80  {
81  results.append( QgsQuickFeatureLayerPair( feature, vl ) );
82  }
83  }
84  if ( mIdentifyMode == IdentifyMode::TopDownStopAtFirst && !results.isEmpty() )
85  {
86  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results with TopDownStopAtFirst mode." ).arg( results.count() ) );
87  return results;
88  }
89  }
90 
91  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results" ).arg( results.count() ) );
92  }
93 
94  return results;
95 }
96 
97 static QgsQuickFeatureLayerPair _closestFeature( const QgsQuickFeatureLayerPairs &results, const QgsMapSettings &mapSettings, const QPointF &point, double searchRadius )
98 {
99  QgsPointXY mapPoint = mapSettings.mapToPixel().toMapCoordinates( point.toPoint() );
100  QgsGeometry mapPointGeom( QgsGeometry::fromPointXY( mapPoint ) );
101 
102  double distMinPoint = 1e10, distMinLine = 1e10, distMinPolygon = 1e10;
103  int iMinPoint = -1, iMinLine = -1, iMinPolygon = -1;
104  for ( int i = 0; i < results.count(); ++i )
105  {
106  const QgsQuickFeatureLayerPair &res = results.at( i );
107  QgsGeometry geom( res.feature().geometry() );
108  try
109  {
110  geom.transform( mapSettings.layerTransform( res.layer() ) );
111  }
112  catch ( QgsCsException &e )
113  {
114  Q_UNUSED( e )
115  // Caught an error in transform
116  continue;
117  }
118 
119  double dist = geom.distance( mapPointGeom );
120  QgsWkbTypes::GeometryType type = QgsWkbTypes::geometryType( geom.wkbType() );
121  if ( type == QgsWkbTypes::PointGeometry )
122  {
123  if ( dist < distMinPoint )
124  {
125  iMinPoint = i;
126  distMinPoint = dist;
127  }
128  }
129  else if ( type == QgsWkbTypes::LineGeometry )
130  {
131  if ( dist < distMinLine )
132  {
133  iMinLine = i;
134  distMinLine = dist;
135  }
136  }
137  else // polygons
138  {
139  if ( dist < distMinPolygon )
140  {
141  iMinPolygon = i;
142  distMinPolygon = dist;
143  }
144  }
145  }
146 
147  // we give priority to points, then lines, then polygons
148  // the rationale is that points in polygon (or on a line) would have nearly
149  // always non-zero distance while polygon surrounding it has zero distance,
150  // so it would be difficult to identify it
151  if ( iMinPoint != -1 && distMinPoint <= searchRadius )
152  return results.at( iMinPoint );
153  else if ( iMinLine != -1 && distMinLine <= searchRadius )
154  return results.at( iMinLine );
155  else if ( iMinPolygon != -1 )
156  return results.at( iMinPolygon );
157  else
158  return QgsQuickFeatureLayerPair();
159 }
160 
162 {
163  QgsQuickFeatureLayerPairs results = identify( point, layer );
164  return _closestFeature( results, mMapSettings->mapSettings(), point, searchRadiusMU() );
165 }
166 
167 QgsFeatureList QgsQuickIdentifyKit::identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const
168 {
169  QgsFeatureList results;
170 
171  if ( !layer || !layer->isSpatial() )
172  return results;
173 
174  if ( !layer->isInScaleRange( mMapSettings->mapSettings().scale() ) )
175  return results;
176 
177  QgsFeatureList featureList;
178 
179  // toLayerCoordinates will throw an exception for an 'invalid' point.
180  // For example, if you project a world map onto a globe using EPSG 2163
181  // and then click somewhere off the globe, an exception will be thrown.
182  try
183  {
184  // create the search rectangle
185  double searchRadius = searchRadiusMU();
186 
187  QgsRectangle r;
188  r.setXMinimum( point.x() - searchRadius );
189  r.setXMaximum( point.x() + searchRadius );
190  r.setYMinimum( point.y() - searchRadius );
191  r.setYMaximum( point.y() + searchRadius );
192 
193  r = toLayerCoordinates( layer, r );
194 
195  QgsFeatureRequest req;
196  req.setFilterRect( r );
197  req.setLimit( mFeaturesLimit );
199 
200  QgsFeatureIterator fit = layer->getFeatures( req );
201  QgsFeature f;
202  while ( fit.nextFeature( f ) )
203  featureList << QgsFeature( f );
204  }
205  catch ( QgsCsException &cse )
206  {
207  QgsDebugMsg( QStringLiteral( "Invalid point, proceed without a found features." ) );
208  Q_UNUSED( cse )
209  }
210 
211  bool filter = false;
212 
213  QgsRenderContext context( QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ) );
214  context.expressionContext() << QgsExpressionContextUtils::layerScope( layer );
215  QgsFeatureRenderer *renderer = layer->renderer();
216  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
217  {
218  // setup scale for scale dependent visibility (rule based)
219  renderer->startRender( context, layer->fields() );
220  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
221  }
222 
223  for ( const QgsFeature &feature : featureList )
224  {
225  context.expressionContext().setFeature( feature );
226 
227  if ( filter && !renderer->willRenderFeature( const_cast<QgsFeature &>( feature ), context ) )
228  continue;
229 
230  results.append( feature );
231  }
232 
233  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
234  {
235  renderer->stopRender( context );
236  }
237 
238  return results;
239 }
240 
241 double QgsQuickIdentifyKit::searchRadiusMU( const QgsRenderContext &context ) const
242 {
243  return mSearchRadiusMm * context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
244 }
245 
246 double QgsQuickIdentifyKit::searchRadiusMU() const
247 {
249  return searchRadiusMU( context );
250 }
251 
252 QgsRectangle QgsQuickIdentifyKit::toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const
253 {
254  return mMapSettings->mapSettings().mapToLayerCoordinates( layer, rect );
255 }
256 
258 {
259  return mSearchRadiusMm;
260 }
261 
262 void QgsQuickIdentifyKit::setSearchRadiusMm( double searchRadiusMm )
263 {
264  if ( qgsDoubleNear( mSearchRadiusMm, searchRadiusMm ) )
265  return;
266 
267  mSearchRadiusMm = searchRadiusMm;
268  emit searchRadiusMmChanged();
269 }
270 
272 {
273  return mFeaturesLimit;
274 }
275 
277 {
278  if ( mFeaturesLimit == limit )
279  return;
280 
281  mFeaturesLimit = limit;
282  emit featuresLimitChanged();
283 }
QgsGeometry::fromPointXY
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
Definition: qgsgeometry.cpp:164
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
qgsexpressioncontextutils.h
QgsFeatureRenderer::Filter
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Definition: qgsrenderer.h:256
QgsPointXY::y
double y
Definition: qgspointxy.h:48
qgsquickidentifykit.h
QgsQuickIdentifyKit::mapSettings
QgsQuickMapSettings mapSettings
Map settings.
Definition: qgsquickidentifykit.h:53
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:309
QgsMapSettings::mapToLayerCoordinates
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
Definition: qgsmapsettings.cpp:516
QgsMapToPixel::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns current map units per pixel.
Definition: qgsmaptopixel.cpp:128
QgsFeatureRequest::ExactIntersect
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition: qgsfeaturerequest.h:109
QgsMapSettings::layerTransform
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
Definition: qgsmapsettings.cpp:418
QgsRenderContext::fromMapSettings
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Definition: qgsrendercontext.cpp:170
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsQuickIdentifyKit::mapSettingsChanged
void mapSettingsChanged()
Map settings.
QgsQuickIdentifyKit::featuresLimitChanged
void featuresLimitChanged()
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
QgsQuickIdentifyKit::identify
Q_INVOKABLE QgsQuickFeatureLayerPairs identify(const QPointF &point, QgsVectorLayer *layer=nullptr)
Gets all features in the search radius.
Definition: qgsquickidentifykit.cpp:47
QgsQuickMapSettings::project
QgsProject project
A project property should be used as a primary source of project all other components in the applicat...
Definition: qgsquickmapsettings.h:57
QgsFeatureRenderer::capabilities
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:274
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:264
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
qgis.h
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsVectorLayer::isSpatial
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Definition: qgsvectorlayer.cpp:3591
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:317
QgsMapToPixel::toMapCoordinates
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
Definition: qgsmaptopixel.cpp:108
QgsQuickMapSettings::mapSettings
QgsMapSettings mapSettings() const
Clone map settings.
Definition: qgsquickmapsettings.cpp:128
QgsQuickFeatureLayerPair::layer
QgsVectorLayer layer
Vector layer to which the feature belongs.
Definition: qgsquickfeaturelayerpair.h:49
QgsRectangle
Definition: qgsrectangle.h:41
QgsFeatureRenderer::stopRender
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
Definition: qgsrenderer.cpp:107
QgsFeatureRenderer::willRenderFeature
virtual bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns whether the renderer will render a feature or not.
Definition: qgsrenderer.cpp:364
QgsFeatureRequest::setFilterRect
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
Definition: qgsfeaturerequest.cpp:97
QgsQuickMapSettings
Definition: qgsquickmapsettings.h:46
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3280
QgsMapLayer::isInScaleRange
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
Definition: qgsmaplayer.cpp:669
QgsFeatureRequest
Definition: qgsfeaturerequest.h:75
QgsMapLayer::flags
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
Definition: qgsmaplayer.cpp:134
QgsQuickFeatureLayerPairs
QList< QgsQuickFeatureLayerPair > QgsQuickFeatureLayerPairs
Definition: qgsquickfeaturelayerpair.h:102
QgsCsException
Definition: qgsexception.h:65
QgsQuickIdentifyKit::searchRadiusMm
double searchRadiusMm
Search radius for the identify functions.
Definition: qgsquickidentifykit.h:60
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:572
QgsWkbTypes::geometryType
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:937
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsQuickIdentifyKit::searchRadiusMmChanged
void searchRadiusMmChanged()
Search radius for the identify functions.
QgsMapSettings::scale
double scale() const
Returns the calculated map scale.
Definition: qgsmapsettings.cpp:395
QgsQuickFeatureLayerPair::feature
QgsFeature feature
Feature that belongs to layer.
Definition: qgsquickfeaturelayerpair.h:56
qgsrenderer.h
qgsvectorlayer.h
QgsPointXY
Definition: qgspointxy.h:43
QgsMapLayer::Identifiable
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:142
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:142
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:141
QgsFeatureRenderer
Definition: qgsrenderer.h:102
QgsWkbTypes::GeometryType
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:373
QgsGeometry
Definition: qgsgeometry.h:122
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsFeatureRenderer::ScaleDependent
@ ScaleDependent
Depends on scale if feature will be rendered (rule based )
Definition: qgsrenderer.h:257
QgsFeatureRequest::setLimit
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Definition: qgsfeaturerequest.cpp:178
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsQuickFeatureLayerPair
Definition: qgsquickfeaturelayerpair.h:40
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsQuickIdentifyKit::featuresLimit
int featuresLimit
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
Definition: qgsquickidentifykit.h:67
QgsQuickIdentifyKit::setFeaturesLimit
void setFeaturesLimit(int limit)
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
Definition: qgsquickidentifykit.cpp:276
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:85
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:281
QgsFeature
Definition: qgsfeature.h:55
QgsQuickIdentifyKit::setSearchRadiusMm
void setSearchRadiusMm(double searchRadiusMm)
Search radius for the identify functions.
Definition: qgsquickidentifykit.cpp:262
QgsQuickIdentifyKit::QgsQuickIdentifyKit
QgsQuickIdentifyKit(QObject *parent=nullptr)
Constructor of new identify kit.
Definition: qgsquickidentifykit.cpp:28
qgslogger.h
QgsMapSettings
Definition: qgsmapsettings.h:86
QgsFeatureRenderer::startRender
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:93
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
qgsquickmapsettings.h
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:184
QgsMapSettings::mapToPixel
const QgsMapToPixel & mapToPixel() const
Definition: qgsmapsettings.h:433
qgsproject.h
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
qgsmessagelog.h
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:881
QgsQuickIdentifyKit::identifyOne
Q_INVOKABLE QgsQuickFeatureLayerPair identifyOne(const QPointF &point, QgsVectorLayer *layer=nullptr)
Gets the closest feature to the point within the search radius.
Definition: qgsquickidentifykit.cpp:161
QgsQuickIdentifyKit::setMapSettings
void setMapSettings(QgsQuickMapSettings *mapSettings)
Map settings.
Definition: qgsquickidentifykit.cpp:38