QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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"
24 
25 #include "qgis.h"
26 
28  : QObject( parent )
29 {
30 }
31 
33 {
34  return mMapSettings;
35 }
36 
38 {
39  if ( mapSettings == mMapSettings )
40  return;
41 
42  mMapSettings = mapSettings;
43  emit mapSettingsChanged();
44 }
45 
47 {
49 
50  if ( !mMapSettings )
51  {
52  QgsDebugMsg( QStringLiteral( "Unable to use IdentifyKit without mapSettings property set." ) );
53  return results;
54  }
55  QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() );
56 
57  if ( layer )
58  {
59  QgsFeatureList featureList = identifyVectorLayer( layer, mapPoint );
60  for ( const QgsFeature &feature : featureList )
61  {
62  results.append( QgsQuickFeatureLayerPair( feature, layer ) );
63  }
64  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) );
65  }
66  else
67  {
68  for ( QgsMapLayer *layer : mMapSettings->mapSettings().layers() )
69  {
70  if ( mMapSettings->project() && !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
71  continue;
72 
73  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
74  if ( vl )
75  {
76  QgsFeatureList featureList = identifyVectorLayer( vl, mapPoint );
77 
78  for ( const QgsFeature &feature : featureList )
79  {
80  results.append( QgsQuickFeatureLayerPair( feature, vl ) );
81  }
82  }
83  if ( mIdentifyMode == IdentifyMode::TopDownStopAtFirst && !results.isEmpty() )
84  {
85  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results with TopDownStopAtFirst mode." ).arg( results.count() ) );
86  return results;
87  }
88  }
89 
90  QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results" ).arg( results.count() ) );
91  }
92 
93  return results;
94 }
95 
96 static QgsQuickFeatureLayerPair _closestFeature( const QgsQuickFeatureLayerPairs &results, const QgsMapSettings &mapSettings, const QPointF &point )
97 {
98  QgsPointXY mapPoint = mapSettings.mapToPixel().toMapCoordinates( point.toPoint() );
99  QgsGeometry mapPointGeom( QgsGeometry::fromPointXY( mapPoint ) );
100 
101  double distMin = 1e10;
102  int iMin = -1;
103  for ( int i = 0; i < results.count(); ++i )
104  {
105  const QgsQuickFeatureLayerPair &res = results.at( i );
106  QgsGeometry geom( res.feature().geometry() );
107  try
108  {
109  geom.transform( mapSettings.layerTransform( res.layer() ) );
110  }
111  catch ( QgsCsException &e )
112  {
113  Q_UNUSED( e );
114  // Caught an error in transform
115  continue;
116  }
117 
118  double dist = geom.distance( mapPointGeom );
119  if ( dist < distMin )
120  {
121  iMin = i;
122  distMin = dist;
123  }
124  }
125 
126  if ( results.empty() )
127  {
128  return QgsQuickFeatureLayerPair();
129  }
130  else
131  {
132  return results.at( iMin );
133  }
134 }
135 
137 {
138  QgsQuickFeatureLayerPairs results = identify( point, layer );
139  return _closestFeature( results, mMapSettings->mapSettings(), point );
140 }
141 
142 QgsFeatureList QgsQuickIdentifyKit::identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const
143 {
144  QgsFeatureList results;
145 
146  if ( !layer || !layer->isSpatial() )
147  return results;
148 
149  if ( !layer->isInScaleRange( mMapSettings->mapSettings().scale() ) )
150  return results;
151 
152  QgsFeatureList featureList;
153 
154  // toLayerCoordinates will throw an exception for an 'invalid' point.
155  // For example, if you project a world map onto a globe using EPSG 2163
156  // and then click somewhere off the globe, an exception will be thrown.
157  try
158  {
159  // create the search rectangle
160  double searchRadius = searchRadiusMU();
161 
162  QgsRectangle r;
163  r.setXMinimum( point.x() - searchRadius );
164  r.setXMaximum( point.x() + searchRadius );
165  r.setYMinimum( point.y() - searchRadius );
166  r.setYMaximum( point.y() + searchRadius );
167 
168  r = toLayerCoordinates( layer, r );
169 
170  QgsFeatureRequest req;
171  req.setFilterRect( r );
172  req.setLimit( mFeaturesLimit );
174 
175  QgsFeatureIterator fit = layer->getFeatures( req );
176  QgsFeature f;
177  while ( fit.nextFeature( f ) )
178  featureList << QgsFeature( f );
179  }
180  catch ( QgsCsException &cse )
181  {
182  QgsDebugMsg( QStringLiteral( "Invalid point, proceed without a found features." ) );
183  Q_UNUSED( cse );
184  }
185 
186  bool filter = false;
187 
188  QgsRenderContext context( QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ) );
190  QgsFeatureRenderer *renderer = layer->renderer();
191  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
192  {
193  // setup scale for scale dependent visibility (rule based)
194  renderer->startRender( context, layer->fields() );
195  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
196  }
197 
198  for ( const QgsFeature &feature : featureList )
199  {
200  context.expressionContext().setFeature( feature );
201 
202  if ( filter && !renderer->willRenderFeature( const_cast<QgsFeature &>( feature ), context ) )
203  continue;
204 
205  results.append( feature );
206  }
207 
208  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
209  {
210  renderer->stopRender( context );
211  }
212 
213  return results;
214 }
215 
216 double QgsQuickIdentifyKit::searchRadiusMU( const QgsRenderContext &context ) const
217 {
218  return mSearchRadiusMm * context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
219 }
220 
221 double QgsQuickIdentifyKit::searchRadiusMU() const
222 {
224  return searchRadiusMU( context );
225 }
226 
227 QgsRectangle QgsQuickIdentifyKit::toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const
228 {
229  return mMapSettings->mapSettings().mapToLayerCoordinates( layer, rect );
230 }
231 
233 {
234  return mSearchRadiusMm;
235 }
236 
238 {
239  if ( qgsDoubleNear( mSearchRadiusMm, searchRadiusMm ) )
240  return;
241 
242  mSearchRadiusMm = searchRadiusMm;
243  emit searchRadiusMmChanged();
244 }
245 
247 {
248  return mFeaturesLimit;
249 }
250 
252 {
253  if ( mFeaturesLimit == limit )
254  return;
255 
256  mFeaturesLimit = limit;
257  emit featuresLimitChanged();
258 }
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:40
Base class for all map layer types.
Definition: qgsmaplayer.h:63
virtual bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns whether the renderer will render a feature or not.
Depends on scale if feature will be rendered (rule based )
Definition: qgsrenderer.h:245
QgsMapSettings mapSettings() const
Clone map settings.
double scale() const
Returns the calculated map scale.
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:134
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
int featuresLimit() const
Maximum number of features returned from the QgsQuickIdentifyKit::identify()
const QgsMapToPixel & mapToPixel() const
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer&#39;s CRS to destination CRS.
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.
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
QgsQuickMapSettings * mapSettings() const
Map settings.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void searchRadiusMmChanged()
Search radius for the identify functions.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:139
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.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QgsFeatureRenderer * renderer()
Returns renderer.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
double x
Definition: qgspointxy.h:47
QgsExpressionContext & expressionContext()
Gets the expression context.
Contains information about the context of a rendering operation.
QgsFeature feature
Feature that belongs to layer.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:92
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:144
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.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
void mapSettingsChanged()
Map settings.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query 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:67
QList< QgsQuickFeatureLayerPair > QgsQuickFeatureLayerPairs
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)
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:130
double searchRadiusMm() const
Search radius for the identify functions.
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:129
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.