QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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() ) );
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 
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 }
void featuresLimitChanged()
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
void setSearchRadiusMm(double searchRadiusMm)
Search radius for the identify functions.
Wrapper for iterator of features from vector data provider or vector layer.
QgsProject project
A project property should be used as a primary source of project all other components in the applicat...
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:78
int featuresLimit() const
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
Depends on scale if feature will be rendered (rule based )
Definition: qgsrenderer.h:245
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.
void setFeaturesLimit(int limit)
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
Use exact geometry intersection (slower) instead of bounding boxes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:244
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsMapSettings mapSettings() const
Clone map settings.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
double searchRadiusMm() const
Search radius for the identify functions.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
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:55
The QgsQuickMapSettings class encapsulates QgsMapSettings class to offer settings of configuration of...
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
void setMapSettings(QgsQuickMapSettings *mapSettings)
Map settings.
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void searchRadiusMmChanged()
Search radius for the identify functions.
double scale() const
Returns the calculated map scale.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
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:666
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.
QgsFeatureRenderer * renderer()
Returns renderer.
double mapUnitsPerPixel() const
Returns current map units per pixel.
const QgsMapToPixel & mapToPixel() const
QgsQuickMapSettings * mapSettings() const
Map settings.
double x
Definition: qgspointxy.h:47
QgsExpressionContext & expressionContext()
Gets the expression context.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
Contains information about the context of a rendering operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QgsFeature feature
Feature that belongs to layer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:93
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Q_INVOKABLE QgsQuickFeatureLayerPair identifyOne(const QPointF &point, QgsVectorLayer *layer=nullptr)
Gets the closest feature to the point within the search radius.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QgsVectorLayer layer
Vector layer to which the feature belongs.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
void mapSettingsChanged()
Map settings.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE QgsQuickFeatureLayerPairs identify(const QPointF &point, QgsVectorLayer *layer=nullptr)
Gets all features in the search radius.
QString name
Definition: qgsmaplayer.h:82
QList< QgsQuickFeatureLayerPair > QgsQuickFeatureLayerPairs
virtual bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns whether the renderer will render a feature or not.
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
Pair of QgsFeature and QgsVectorLayer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Represents a vector layer which manages a vector based data sets.
QgsQuickIdentifyKit(QObject *parent=nullptr)
Constructor of new identify kit.
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:136
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:262
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.