QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
qgsmaptoolidentify.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptoolidentify.cpp - map tool for identifying features
3  ---------------------
4  begin : January 2006
5  copyright : (C) 2006 by Martin Dobias
6  email : wonder.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 "qgsapplication.h"
17 #include "qgsdistancearea.h"
18 #include "qgsfeature.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgsfeaturestore.h"
21 #include "qgsfields.h"
22 #include "qgsgeometry.h"
23 #include "qgsgeometryengine.h"
24 #include "qgsidentifymenu.h"
25 #include "qgslogger.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsmaptoolidentify.h"
28 #include "qgsmeshlayer.h"
29 #include "qgsmaplayer.h"
30 #include "qgsrasterdataprovider.h"
31 #include "qgsrasterlayer.h"
34 #include "qgsvectordataprovider.h"
35 #include "qgsvectorlayer.h"
37 #include "qgsvectortilelayer.h"
38 #include "qgsvectortileloader.h"
40 #include "qgsvectortileutils.h"
41 #include "qgsproject.h"
42 #include "qgsrenderer.h"
43 #include "qgstiles.h"
44 #include "qgsgeometryutils.h"
45 #include "qgsgeometrycollection.h"
46 #include "qgscurve.h"
47 #include "qgscoordinateutils.h"
48 #include "qgsexception.h"
49 #include "qgssettings.h"
51 #include "qgspointcloudlayer.h"
52 #include "qgspointcloudrenderer.h"
55 #include "qgssymbol.h"
56 #include "qgsguiutils.h"
57 
58 #include <QMouseEvent>
59 #include <QCursor>
60 #include <QPixmap>
61 #include <QStatusBar>
62 #include <QVariant>
63 
65  : QgsMapTool( canvas )
66  , mIdentifyMenu( new QgsIdentifyMenu( mCanvas ) )
67  , mLastMapUnitsPerPixel( -1.0 )
68  , mCoordinatePrecision( 6 )
69 {
70  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Identify ) );
71 }
72 
74 {
75  delete mIdentifyMenu;
76 }
77 
79 {
80  Q_UNUSED( e )
81 }
82 
84 {
85  Q_UNUSED( e )
86 }
87 
89 {
90  Q_UNUSED( e )
91 }
92 
93 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, const QList<QgsMapLayer *> &layerList, IdentifyMode mode, const QgsIdentifyContext &identifyContext )
94 {
95  return identify( x, y, mode, layerList, AllLayers, identifyContext );
96 }
97 
98 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, LayerType layerType, const QgsIdentifyContext &identifyContext )
99 {
100  return identify( x, y, mode, QList<QgsMapLayer *>(), layerType, identifyContext );
101 }
102 
103 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType, const QgsIdentifyContext &identifyContext )
104 {
105  return identify( QgsGeometry::fromPointXY( toMapCoordinates( QPoint( x, y ) ) ), mode, layerList, layerType, identifyContext );
106 }
107 
108 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, LayerType layerType, const QgsIdentifyContext &identifyContext )
109 {
110  return identify( geometry, mode, QList<QgsMapLayer *>(), layerType, identifyContext );
111 }
112 
113 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType, const QgsIdentifyContext &identifyContext )
114 {
115  QList<IdentifyResult> results;
116 
117  mLastGeometry = geometry;
118  mLastExtent = mCanvas->extent();
119  mLastMapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
120 
121  mCoordinatePrecision = QgsCoordinateUtils::calculateCoordinatePrecision( mLastMapUnitsPerPixel, mCanvas->mapSettings().destinationCrs() );
122 
123  if ( mode == DefaultQgsSetting )
124  {
125  QgsSettings settings;
126  mode = settings.enumValue( QStringLiteral( "Map/identifyMode" ), ActiveLayer );
127  }
128 
129  if ( mode == LayerSelection )
130  {
131  QPoint canvasPt = toCanvasCoordinates( geometry.asPoint() );
132  int x = canvasPt.x(), y = canvasPt.y();
133  QList<IdentifyResult> results = identify( x, y, TopDownAll, layerList, layerType, identifyContext );
134  QPoint globalPos = mCanvas->mapToGlobal( QPoint( x + 5, y + 5 ) );
135  return mIdentifyMenu->exec( results, globalPos );
136  }
137  else if ( mode == ActiveLayer && layerList.isEmpty() )
138  {
139  QgsMapLayer *layer = mCanvas->currentLayer();
140 
141  if ( !layer )
142  {
143  emit identifyMessage( tr( "No active layer. To identify features, you must choose an active layer." ) );
144  return results;
145  }
146  if ( !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
147  return results;
148 
149  QApplication::setOverrideCursor( Qt::WaitCursor );
150 
151  identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType, identifyContext );
152  }
153  else
154  {
155  QApplication::setOverrideCursor( Qt::WaitCursor );
156 
157  QList< QgsMapLayer * > targetLayers;
158  if ( layerList.isEmpty() )
159  targetLayers = mCanvas->layers( true );
160  else
161  targetLayers = layerList;
162 
163  const int layerCount = targetLayers.size();
164  for ( int i = 0; i < layerCount; i++ )
165  {
166  QgsMapLayer *layer = targetLayers.value( i );
167 
168  emit identifyProgress( i, layerCount );
169  emit identifyMessage( tr( "Identifying on %1…" ).arg( layer->name() ) );
170 
171  if ( !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
172  continue;
173 
174  if ( identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType, identifyContext ) )
175  {
176  if ( mode == TopDownStopAtFirst )
177  break;
178  }
179  }
180 
181  emit identifyProgress( layerCount, layerCount );
182  emit identifyMessage( tr( "Identifying done." ) );
183  }
184 
185  QApplication::restoreOverrideCursor();
186 
187  return results;
188 }
189 
190 void QgsMapToolIdentify::setCanvasPropertiesOverrides( double searchRadiusMapUnits )
191 {
192  mOverrideCanvasSearchRadius = searchRadiusMapUnits;
193 }
194 
196 {
197  mOverrideCanvasSearchRadius = -1;
198 }
199 
201 {
203 }
204 
206 {
208 }
209 
210 bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType, const QgsIdentifyContext &identifyContext )
211 {
212  return identifyLayer( results, layer, QgsGeometry::fromPointXY( point ), viewExtent, mapUnitsPerPixel, layerType, identifyContext );
213 }
214 
215 bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType, const QgsIdentifyContext &identifyContext )
216 {
217  switch ( layer->type() )
218  {
220  if ( layerType.testFlag( VectorLayer ) )
221  {
222  return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), geometry, identifyContext );
223  }
224  break;
225 
227  if ( layerType.testFlag( RasterLayer ) )
228  {
229  return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), geometry, viewExtent, mapUnitsPerPixel, identifyContext );
230  }
231  break;
232 
234  if ( layerType.testFlag( MeshLayer ) )
235  {
236  return identifyMeshLayer( results, qobject_cast<QgsMeshLayer *>( layer ), geometry, identifyContext );
237  }
238  break;
239 
241  if ( layerType.testFlag( VectorTileLayer ) )
242  {
243  return identifyVectorTileLayer( results, qobject_cast<QgsVectorTileLayer *>( layer ), geometry, identifyContext );
244  }
245  break;
246 
248  if ( layerType.testFlag( PointCloudLayer ) )
249  {
250  return identifyPointCloudLayer( results, qobject_cast<QgsPointCloudLayer *>( layer ), geometry, identifyContext );
251  }
252  break;
253 
254  // not supported
259  break;
260  }
261  return false;
262 }
263 
264 bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext )
265 {
266  return identifyVectorLayer( results, layer, QgsGeometry::fromPointXY( point ), identifyContext );
267 }
268 
269 bool QgsMapToolIdentify::identifyMeshLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMeshLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
270 {
271  const QgsPointXY point = geometry.asPoint(); // mesh layers currently only support identification by point
272  return identifyMeshLayer( results, layer, point, identifyContext );
273 }
274 
275 bool QgsMapToolIdentify::identifyMeshLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMeshLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext )
276 {
277  QgsDebugMsgLevel( "point = " + point.toString(), 4 );
278  if ( !layer )
279  return false;
280 
281  double searchRadius = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
282  bool isTemporal = identifyContext.isTemporal() && layer->temporalProperties()->isActive();
283 
284  QList<QgsMeshDatasetIndex> datasetIndexList;
285  int activeScalarGroup = layer->rendererSettings().activeScalarDatasetGroup();
286  int activeVectorGroup = layer->rendererSettings().activeVectorDatasetGroup();
287 
288  const QList<int> allGroup = layer->enabledDatasetGroupsIndexes();
289  if ( isTemporal ) //non active dataset group value are only accessible if temporal is active
290  {
291  const QgsDateTimeRange &time = identifyContext.temporalRange();
292  if ( activeScalarGroup >= 0 )
293  datasetIndexList.append( layer->activeScalarDatasetAtTime( time ) );
294  if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
295  datasetIndexList.append( layer->activeVectorDatasetAtTime( time ) );
296 
297  for ( int groupIndex : allGroup )
298  {
299  if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
300  datasetIndexList.append( layer->datasetIndexAtTime( time, groupIndex ) );
301  }
302  }
303  else
304  {
305  // only active dataset group
306  if ( activeScalarGroup >= 0 )
307  datasetIndexList.append( layer->staticScalarDatasetIndex() );
308  if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
309  datasetIndexList.append( layer->staticVectorDatasetIndex() );
310 
311  // ...and static dataset group
312  for ( int groupIndex : allGroup )
313  {
314  if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
315  {
316  if ( !layer->datasetGroupMetadata( groupIndex ).isTemporal() )
317  datasetIndexList.append( groupIndex );
318  }
319  }
320  }
321 
322  //create results
323  for ( const QgsMeshDatasetIndex &index : datasetIndexList )
324  {
325  if ( !index.isValid() )
326  continue;
327 
328  const QgsMeshDatasetGroupMetadata &groupMeta = layer->datasetGroupMetadata( index );
329  QMap< QString, QString > derivedAttributes;
330 
331  QMap<QString, QString> attribute;
332  if ( groupMeta.isScalar() )
333  {
334  const QgsMeshDatasetValue scalarValue = layer->datasetValue( index, point, searchRadius );
335  const double scalar = scalarValue.scalar();
336  attribute.insert( tr( "Scalar Value" ), std::isnan( scalar ) ? tr( "no data" ) : QLocale().toString( scalar ) );
337  }
338 
339  if ( groupMeta.isVector() )
340  {
341  const QgsMeshDatasetValue vectorValue = layer->datasetValue( index, point, searchRadius );
342  const double vectorX = vectorValue.x();
343  const double vectorY = vectorValue.y();
344  if ( std::isnan( vectorX ) || std::isnan( vectorY ) )
345  attribute.insert( tr( "Vector Value" ), tr( "no data" ) );
346  else
347  {
348  attribute.insert( tr( "Vector Magnitude" ), QLocale().toString( vectorValue.scalar() ) );
349  derivedAttributes.insert( tr( "Vector x-component" ), QLocale().toString( vectorY ) );
350  derivedAttributes.insert( tr( "Vector y-component" ), QLocale().toString( vectorX ) );
351  }
352  }
353 
354  const QgsMeshDatasetMetadata &meta = layer->datasetMetadata( index );
355 
356  if ( groupMeta.isTemporal() )
357  derivedAttributes.insert( tr( "Time Step" ), layer->formatTime( meta.time() ) );
358  derivedAttributes.insert( tr( "Source" ), groupMeta.uri() );
359 
360  QString resultName = groupMeta.name();
361  if ( isTemporal && ( index.group() == activeScalarGroup || index.group() == activeVectorGroup ) )
362  resultName.append( tr( " (active)" ) );
363 
364  const IdentifyResult result( layer,
365  resultName,
366  attribute,
367  derivedAttributes );
368 
369  results->append( result );
370  }
371 
372  QMap<QString, QString> derivedGeometry;
373 
374  QgsPointXY vertexPoint = layer->snapOnElement( QgsMesh::Vertex, point, searchRadius );
375  if ( !vertexPoint.isEmpty() )
376  {
377  derivedGeometry.insert( tr( "Snapped Vertex Position X" ), QLocale().toString( vertexPoint.x() ) );
378  derivedGeometry.insert( tr( "Snapped Vertex Position Y" ), QLocale().toString( vertexPoint.y() ) );
379  }
380 
381  QgsPointXY faceCentroid = layer->snapOnElement( QgsMesh::Face, point, searchRadius );
382  if ( !faceCentroid.isEmpty() )
383  {
384  derivedGeometry.insert( tr( "Face Centroid X" ), QLocale().toString( faceCentroid.x() ) );
385  derivedGeometry.insert( tr( "Face Centroid Y" ), QLocale().toString( faceCentroid.y() ) );
386  }
387 
388  QgsPointXY pointOnEdge = layer->snapOnElement( QgsMesh::Edge, point, searchRadius );
389  if ( !pointOnEdge.isEmpty() )
390  {
391  derivedGeometry.insert( tr( "Point on Edge X" ), QLocale().toString( pointOnEdge.x() ) );
392  derivedGeometry.insert( tr( "Point on Edge Y" ), QLocale().toString( pointOnEdge.y() ) );
393  }
394 
395  const IdentifyResult result( layer,
396  tr( "Geometry" ),
398  derivedGeometry );
399 
400  results->append( result );
401 
402  return true;
403 }
404 
405 bool QgsMapToolIdentify::identifyVectorTileLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorTileLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
406 {
407  Q_UNUSED( identifyContext )
408  if ( !layer || !layer->isSpatial() )
409  return false;
410 
411  if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
412  {
413  QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
414  return false;
415  }
416 
417  QgsTemporaryCursorOverride waitCursor( Qt::WaitCursor );
418 
419  QMap< QString, QString > commonDerivedAttributes;
420 
421  QgsGeometry selectionGeom = geometry;
422  bool isPointOrRectangle;
423  QgsPointXY point;
424  bool isSingleClick = selectionGeom.type() == Qgis::GeometryType::Point;
425  if ( isSingleClick )
426  {
427  isPointOrRectangle = true;
428  point = selectionGeom.asPoint();
429 
430  commonDerivedAttributes = derivedAttributesForPoint( QgsPoint( point ) );
431  }
432  else
433  {
434  // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
435  isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
436  }
437 
438  int featureCount = 0;
439 
440  std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
441 
442  // toLayerCoordinates will throw an exception for an 'invalid' point.
443  // For example, if you project a world map onto a globe using EPSG 2163
444  // and then click somewhere off the globe, an exception will be thrown.
445  try
446  {
447  QgsRectangle r;
448  if ( isSingleClick )
449  {
450  double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
451  r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
452  }
453  else
454  {
455  r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
456 
457  if ( !isPointOrRectangle )
458  {
459  QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), layer->crs(), mCanvas->mapSettings().transformContext() );
460  if ( ct.isValid() )
461  selectionGeom.transform( ct );
462 
463  // use prepared geometry for faster intersection test
464  selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
465  }
466  }
467 
468  const double tileScale = layer->tileMatrixSet().calculateTileScaleForMap(
469  mCanvas->scale(),
470  mCanvas->mapSettings().destinationCrs(),
471  mCanvas->mapSettings().extent(),
472  mCanvas->size(),
473  mCanvas->logicalDpiX() );
474 
475  const int tileZoom = layer->tileMatrixSet().scaleToZoomLevel( tileScale );
476  const QgsTileMatrix tileMatrix = layer->tileMatrixSet().tileMatrix( tileZoom );
477  const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
478 
479  const QVector< QgsTileXYZ> tiles = layer->tileMatrixSet().tilesInRange( tileRange, tileZoom );
480 
481  for ( const QgsTileXYZ &tileID : tiles )
482  {
483  const QgsVectorTileRawData data = layer->getRawTile( tileID );
484  if ( data.data.isEmpty() )
485  continue; // failed to get data
486 
487  QgsVectorTileMVTDecoder decoder( layer->tileMatrixSet() );
488  if ( !decoder.decode( data ) )
489  continue; // failed to decode
490 
491  QMap<QString, QgsFields> perLayerFields;
492  const QStringList layerNames = decoder.layers();
493  for ( const QString &layerName : layerNames )
494  {
495  QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
496  perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
497  }
498 
499  const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
500  const QStringList featuresLayerNames = features.keys();
501  for ( const QString &layerName : featuresLayerNames )
502  {
503  const QgsFields fFields = perLayerFields[layerName];
504  const QVector<QgsFeature> &layerFeatures = features[layerName];
505  for ( const QgsFeature &f : layerFeatures )
506  {
507  if ( f.geometry().intersects( r ) && ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) ) )
508  {
509  QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
510  derivedAttributes.insert( tr( "Feature ID" ), FID_TO_STRING( f.id() ) );
511  derivedAttributes.insert( tr( "Tile column" ), QString::number( tileID.column() ) );
512  derivedAttributes.insert( tr( "Tile row" ), QString::number( tileID.row() ) );
513  derivedAttributes.insert( tr( "Tile zoom" ), QString::number( tileID.zoomLevel() ) );
514 
515  results->append( IdentifyResult( layer, layerName, fFields, f, derivedAttributes ) );
516 
517  featureCount++;
518  }
519  }
520  }
521  }
522  }
523  catch ( QgsCsException &cse )
524  {
525  Q_UNUSED( cse )
526  // catch exception for 'invalid' point and proceed with no features found
527  QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
528  }
529 
530  return featureCount > 0;
531 }
532 
533 bool QgsMapToolIdentify::identifyPointCloudLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsPointCloudLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
534 {
535  if ( !identifyContext.zRange().isInfinite() )
536  {
537  if ( !layer->elevationProperties()->isVisibleInZRange( identifyContext.zRange() ) )
538  return false;
539  }
540 
541  QgsPointCloudRenderer *renderer = layer->renderer();
542 
543  QgsRenderContext context = QgsRenderContext::fromMapSettings( mCanvas->mapSettings() );
544  context.setCoordinateTransform( QgsCoordinateTransform( layer->crs(), mCanvas->mapSettings().destinationCrs(), mCanvas->mapSettings().transformContext() ) );
545  if ( !identifyContext.zRange().isInfinite() )
546  context.setZRange( identifyContext.zRange() );
547 
548  const double searchRadiusMapUnits = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
549 
550  const QVector<QVariantMap> points = renderer->identify( layer, context, geometry, searchRadiusMapUnits );
551 
553 
554  return true;
555 }
556 
557 QMap<QString, QString> QgsMapToolIdentify::derivedAttributesForPoint( const QgsPoint &point )
558 {
559  QMap< QString, QString > derivedAttributes;
560 
561  QString x;
562  QString y;
563  formatCoordinate( point, x, y );
564 
565  derivedAttributes.insert( tr( "(clicked coordinate X)" ), x );
566  derivedAttributes.insert( tr( "(clicked coordinate Y)" ), y );
567  if ( point.is3D() )
568  derivedAttributes.insert( tr( "(clicked coordinate Z)" ), QLocale().toString( point.z(), 'f' ) );
569  return derivedAttributes;
570 }
571 
572 bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
573 {
574  if ( !layer || !layer->isSpatial() || !layer->dataProvider() )
575  return false;
576 
577  if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
578  {
579  QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
580  return false;
581  }
582 
583  QString temporalFilter;
584  if ( identifyContext.isTemporal() )
585  {
586  if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
587  return false;
588 
589  QgsVectorLayerTemporalContext temporalContext;
590  temporalContext.setLayer( layer );
591  temporalFilter = qobject_cast< const QgsVectorLayerTemporalProperties * >( layer->temporalProperties() )->createFilterString( temporalContext, identifyContext.temporalRange() );
592  }
593 
594  const bool fetchFeatureSymbols = layer->dataProvider()->capabilities() & QgsVectorDataProvider::FeatureSymbology;
595 
596  QApplication::setOverrideCursor( Qt::WaitCursor );
597 
598  QMap< QString, QString > commonDerivedAttributes;
599 
600  QgsGeometry selectionGeom = geometry;
601  bool isPointOrRectangle;
602  QgsPoint point;
603  bool isSingleClick = selectionGeom.type() == Qgis::GeometryType::Point;
604  if ( isSingleClick )
605  {
606  isPointOrRectangle = true;
607  point = *qgsgeometry_cast< const QgsPoint *>( selectionGeom.constGet() );
608 
609  commonDerivedAttributes = derivedAttributesForPoint( point );
610  }
611  else
612  {
613  // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
614  isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
615  }
616 
617  QgsFeatureList featureList;
618  std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
619 
620  // toLayerCoordinates will throw an exception for an 'invalid' point.
621  // For example, if you project a world map onto a globe using EPSG 2163
622  // and then click somewhere off the globe, an exception will be thrown.
623  try
624  {
625  QgsRectangle r;
626  if ( isSingleClick )
627  {
628  double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
629  r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
630  }
631  else
632  {
633  r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
634 
635  if ( !isPointOrRectangle )
636  {
637  QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), layer->crs(), mCanvas->mapSettings().transformContext() );
638  if ( ct.isValid() )
639  selectionGeom.transform( ct );
640 
641  // use prepared geometry for faster intersection test
642  selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
643  }
644  }
645 
646  QgsFeatureRequest featureRequest;
647  featureRequest.setFilterRect( r );
649  if ( !temporalFilter.isEmpty() )
650  featureRequest.setFilterExpression( temporalFilter );
651 
652  QgsFeatureIterator fit = layer->getFeatures( featureRequest );
653  QgsFeature f;
654  while ( fit.nextFeature( f ) )
655  {
656  if ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) )
657  featureList << QgsFeature( f );
658  }
659  }
660  catch ( QgsCsException &cse )
661  {
662  Q_UNUSED( cse )
663  // catch exception for 'invalid' point and proceed with no features found
664  QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
665  }
666 
667  bool filter = false;
668 
669  QgsRenderContext context( QgsRenderContext::fromMapSettings( mCanvas->mapSettings() ) );
670  context.setExpressionContext( mCanvas->createExpressionContext() );
672  std::unique_ptr< QgsFeatureRenderer > renderer( layer->renderer() ? layer->renderer()->clone() : nullptr );
673  if ( renderer )
674  {
675  // setup scale for scale dependent visibility (rule based)
676  renderer->startRender( context, layer->fields() );
677  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
678  }
679 
680  // When not single click identify, pass an empty point so some derived attributes may still be computed
681  if ( !isSingleClick )
682  point = QgsPoint();
683 
684  const int featureCount = identifyVectorLayer( results, layer, featureList, filter ? renderer.get() : nullptr, commonDerivedAttributes,
685  [point, layer, this]( const QgsFeature & feature )->QMap< QString, QString >
686  {
687  return featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) );
688  }, context );
689 
690  if ( renderer )
691  {
692  renderer->stopRender( context );
693  }
694  QApplication::restoreOverrideCursor();
695  return featureCount > 0;
696 }
697 
698 int QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsFeatureList &features, QgsFeatureRenderer *renderer, const QMap< QString, QString > &commonDerivedAttributes, const std::function< QMap< QString, QString > ( const QgsFeature & ) > &deriveAttributesForFeature, QgsRenderContext &context )
699 {
700  int featureCount = 0;
701  for ( const QgsFeature &feature : std::as_const( features ) )
702  {
703  QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
704 
705  QgsFeatureId fid = feature.id();
706  context.expressionContext().setFeature( feature );
707 
708  if ( renderer && !renderer->willRenderFeature( feature, context ) )
709  continue;
710 
711  derivedAttributes.insert( deriveAttributesForFeature( feature ) );
712  derivedAttributes.insert( tr( "Feature ID" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) );
713 
714  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), feature, derivedAttributes ) );
715  featureCount++;
716  }
717  return featureCount;
718 }
719 
720 void QgsMapToolIdentify::closestVertexAttributes( const QgsAbstractGeometry &geometry, QgsVertexId vId, QgsMapLayer *layer, QMap< QString, QString > &derivedAttributes )
721 {
722  if ( ! vId.isValid( ) )
723  {
724  // We should not get here ...
725  QgsDebugError( "Invalid vertex id!" );
726  return;
727  }
728 
729  QString str = QLocale().toString( vId.vertex + 1 );
730  derivedAttributes.insert( tr( "Closest vertex number" ), str );
731 
732  QgsPoint closestPoint = geometry.vertexAt( vId );
733 
734  QgsPoint closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, closestPoint );
735 
736  QString x;
737  QString y;
738  formatCoordinate( closestPointMapCoords, x, y );
739  derivedAttributes.insert( tr( "Closest vertex X" ), x );
740  derivedAttributes.insert( tr( "Closest vertex Y" ), y );
741 
742  if ( closestPoint.is3D() )
743  {
744  str = QLocale().toString( closestPointMapCoords.z(), 'g', 10 );
745  derivedAttributes.insert( tr( "Closest vertex Z" ), str );
746  }
747  if ( closestPoint.isMeasure() )
748  {
749  str = QLocale().toString( closestPointMapCoords.m(), 'g', 10 );
750  derivedAttributes.insert( tr( "Closest vertex M" ), str );
751  }
752 
753  if ( vId.type == Qgis::VertexType::Curve )
754  {
755  double radius, centerX, centerY;
756  QgsVertexId vIdBefore = vId;
757  --vIdBefore.vertex;
758  QgsVertexId vIdAfter = vId;
759  ++vIdAfter.vertex;
760  QgsGeometryUtils::circleCenterRadius( geometry.vertexAt( vIdBefore ), geometry.vertexAt( vId ),
761  geometry.vertexAt( vIdAfter ), radius, centerX, centerY );
762  derivedAttributes.insert( QStringLiteral( "Closest vertex radius" ), QLocale().toString( radius ) );
763  }
764 }
765 
766 void QgsMapToolIdentify::closestPointAttributes( const QgsAbstractGeometry &geometry, const QgsPointXY &layerPoint, QMap<QString, QString> &derivedAttributes )
767 {
768  QgsPoint closestPoint = QgsGeometryUtils::closestPoint( geometry, QgsPoint( layerPoint ) );
769 
770  QString x;
771  QString y;
772  formatCoordinate( closestPoint, x, y );
773  derivedAttributes.insert( tr( "Closest X" ), x );
774  derivedAttributes.insert( tr( "Closest Y" ), y );
775 
776  if ( closestPoint.is3D() )
777  {
778  const QString str = QLocale().toString( closestPoint.z(), 'g', 10 );
779  derivedAttributes.insert( tr( "Interpolated Z" ), str );
780  }
781  if ( closestPoint.isMeasure() )
782  {
783  const QString str = QLocale().toString( closestPoint.m(), 'g', 10 );
784  derivedAttributes.insert( tr( "Interpolated M" ), str );
785  }
786 }
787 
788 void QgsMapToolIdentify::formatCoordinate( const QgsPointXY &canvasPoint, QString &x, QString &y ) const
789 {
790  QgsCoordinateUtils::formatCoordinatePartsForProject( QgsProject::instance(), canvasPoint, mCanvas->mapSettings().destinationCrs(),
791  mCoordinatePrecision, x, y );
792 }
793 
794 QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( const QgsFeature &feature, QgsMapLayer *layer, const QgsPointXY &layerPoint )
795 {
796  // Calculate derived attributes and insert:
797  // measure distance or area depending on geometry type
798  QMap< QString, QString > derivedAttributes;
799 
800  // init distance/area calculator
801  QString ellipsoid = QgsProject::instance()->ellipsoid();
802  QgsDistanceArea calc;
803  calc.setEllipsoid( ellipsoid );
805 
808 
809  QgsVertexId vId;
810  QgsPoint closestPoint;
811  if ( feature.hasGeometry() )
812  {
813  geometryType = feature.geometry().type();
814  wkbType = feature.geometry().wkbType();
815  if ( !layerPoint.isEmpty() )
816  {
817  //find closest vertex to clicked point
818  closestPoint = QgsGeometryUtils::closestVertex( *feature.geometry().constGet(), QgsPoint( layerPoint ), vId );
819  }
820  }
821 
822  if ( QgsWkbTypes::isMultiType( wkbType ) )
823  {
824  QString str = QLocale().toString( static_cast<const QgsGeometryCollection *>( feature.geometry().constGet() )->numGeometries() );
825  derivedAttributes.insert( tr( "Parts" ), str );
826  if ( !layerPoint.isEmpty() )
827  {
828  str = QLocale().toString( vId.part + 1 );
829  derivedAttributes.insert( tr( "Part number" ), str );
830  }
831  }
832 
833  Qgis::DistanceUnit cartesianDistanceUnits = QgsUnitTypes::unitType( layer->crs().mapUnits() ) == QgsUnitTypes::unitType( displayDistanceUnits() )
834  ? displayDistanceUnits() : layer->crs().mapUnits();
836  ? displayAreaUnits() : QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() );
837 
838  if ( geometryType == Qgis::GeometryType::Line )
839  {
840  const QgsAbstractGeometry *geom = feature.geometry().constGet();
841 
842  double dist = calc.measureLength( feature.geometry() );
843  dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
844  QString str;
845  if ( ellipsoid != geoNone() )
846  {
847  str = formatDistance( dist );
848  derivedAttributes.insert( tr( "Length (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
849  }
850 
851  str = formatDistance( geom->length()
852  * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
853  if ( QgsWkbTypes::hasZ( geom->wkbType() )
855  {
856  // 3d linestring (or multiline)
857  derivedAttributes.insert( tr( "Length (Cartesian — 2D)" ), str );
858 
859  double totalLength3d = std::accumulate( geom->const_parts_begin(), geom->const_parts_end(), 0.0, []( double total, const QgsAbstractGeometry * part )
860  {
861  return total + qgsgeometry_cast< const QgsLineString * >( part )->length3D();
862  } );
863 
864  str = formatDistance( totalLength3d, cartesianDistanceUnits );
865  derivedAttributes.insert( tr( "Length (Cartesian — 3D)" ), str );
866  }
867  else
868  {
869  derivedAttributes.insert( tr( "Length (Cartesian)" ), str );
870  }
871 
872  str = QLocale().toString( geom->nCoordinates() );
873  derivedAttributes.insert( tr( "Vertices" ), str );
874  if ( !layerPoint.isEmpty() )
875  {
876  //add details of closest vertex to identify point
877  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
878  closestPointAttributes( *geom, layerPoint, derivedAttributes );
879  }
880 
881  if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
882  {
883  // Add the start and end points in as derived attributes
884  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->startPoint().x(), curve->startPoint().y() ) );
885  QString x;
886  QString y;
887  formatCoordinate( pnt, x, y );
888  derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), x );
889  derivedAttributes.insert( tr( "firstY" ), y );
890  pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->endPoint().x(), curve->endPoint().y() ) );
891  formatCoordinate( pnt, x, y );
892  derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), x );
893  derivedAttributes.insert( tr( "lastY" ), y );
894  }
895  }
896  else if ( geometryType == Qgis::GeometryType::Polygon )
897  {
898  double area = calc.measureArea( feature.geometry() );
899  area = calc.convertAreaMeasurement( area, displayAreaUnits() );
900  QString str;
901  if ( ellipsoid != geoNone() )
902  {
903  str = formatArea( area );
904  derivedAttributes.insert( tr( "Area (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
905  }
906  str = formatArea( feature.geometry().area()
907  * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() ), cartesianAreaUnits ), cartesianAreaUnits );
908  derivedAttributes.insert( tr( "Area (Cartesian)" ), str );
909 
910  if ( ellipsoid != geoNone() )
911  {
912  double perimeter = calc.measurePerimeter( feature.geometry() );
913  perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
914  str = formatDistance( perimeter );
915  derivedAttributes.insert( tr( "Perimeter (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
916  }
917  str = formatDistance( feature.geometry().constGet()->perimeter()
918  * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
919  derivedAttributes.insert( tr( "Perimeter (Cartesian)" ), str );
920 
921  str = QLocale().toString( feature.geometry().constGet()->nCoordinates() );
922  derivedAttributes.insert( tr( "Vertices" ), str );
923 
924  if ( !layerPoint.isEmpty() )
925  {
926  //add details of closest vertex to identify point
927  closestVertexAttributes( *feature.geometry().constGet(), vId, layer, derivedAttributes );
928  closestPointAttributes( *feature.geometry().constGet(), layerPoint, derivedAttributes );
929  }
930  }
931  else if ( geometryType == Qgis::GeometryType::Point )
932  {
933  if ( QgsWkbTypes::flatType( wkbType ) == Qgis::WkbType::Point )
934  {
935  // Include the x and y coordinates of the point as a derived attribute
936  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, feature.geometry().asPoint() );
937  QString x;
938  QString y;
939  formatCoordinate( pnt, x, y );
940  derivedAttributes.insert( tr( "X" ), x );
941  derivedAttributes.insert( tr( "Y" ), y );
942 
943  if ( QgsWkbTypes::hasZ( wkbType ) )
944  {
945  const QString str = QLocale().toString( static_cast<const QgsPoint *>( feature.geometry().constGet() )->z(), 'g', 10 );
946  derivedAttributes.insert( tr( "Z" ), str );
947  }
948  if ( QgsWkbTypes::hasM( wkbType ) )
949  {
950  const QString str = QLocale().toString( static_cast<const QgsPoint *>( feature.geometry().constGet() )->m(), 'g', 10 );
951  derivedAttributes.insert( tr( "M" ), str );
952  }
953  }
954  else
955  {
956  //multipart
957 
958  if ( !layerPoint.isEmpty() )
959  {
960  //add details of closest vertex to identify point
961  const QgsAbstractGeometry *geom = feature.geometry().constGet();
962  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
963  }
964  }
965  }
966 
967  if ( feature.embeddedSymbol() )
968  {
969  derivedAttributes.insert( tr( "Embedded Symbol" ), tr( "%1 (%2)" ).arg( QgsSymbol::symbolTypeToString( feature.embeddedSymbol()->type() ), feature.embeddedSymbol()->color().name() ) );
970  }
971 
972  return derivedAttributes;
973 }
974 
975 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
976 {
977  QgsPointXY point = geometry.asPoint(); // raster layers currently only support identification by point
978  return identifyRasterLayer( results, layer, point, viewExtent, mapUnitsPerPixel, identifyContext );
979 }
980 
981 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
982 {
983  QgsDebugMsgLevel( "point = " + point.toString(), 2 );
984  if ( !layer )
985  return false;
986 
987  std::unique_ptr< QgsRasterDataProvider > dprovider( layer->dataProvider()->clone() );
988  if ( !dprovider )
989  return false;
990 
991  int capabilities = dprovider->capabilities();
992  if ( !( capabilities & QgsRasterDataProvider::Identify ) )
993  return false;
994 
995  if ( identifyContext.isTemporal() )
996  {
997  if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
998  return false;
999 
1000  dprovider->temporalCapabilities()->setRequestedTemporalRange( identifyContext.temporalRange() );
1001  }
1002 
1003  if ( !identifyContext.zRange().isInfinite() )
1004  {
1005  if ( !layer->elevationProperties()->isVisibleInZRange( identifyContext.zRange() ) )
1006  return false;
1007  }
1008 
1009  QgsPointXY pointInCanvasCrs = point;
1010  try
1011  {
1012  point = toLayerCoordinates( layer, point );
1013  }
1014  catch ( QgsCsException &cse )
1015  {
1016  Q_UNUSED( cse )
1017  QgsDebugError( QStringLiteral( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
1018  return false;
1019  }
1020  QgsDebugMsgLevel( QStringLiteral( "point = %1 %2" ).arg( point.x() ).arg( point.y() ), 2 );
1021 
1022  if ( !layer->extent().contains( point ) )
1023  return false;
1024 
1025  QMap< QString, QString > attributes, derivedAttributes;
1026 
1027  Qgis::RasterIdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
1028 
1029  // check if the format is really supported otherwise use first supported format
1030  if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) )
1031  {
1032  if ( capabilities & QgsRasterInterface::IdentifyFeature )
1034  else if ( capabilities & QgsRasterInterface::IdentifyValue )
1036  else if ( capabilities & QgsRasterInterface::IdentifyHtml )
1038  else if ( capabilities & QgsRasterInterface::IdentifyText )
1040  else return false;
1041  }
1042 
1043  QgsRasterIdentifyResult identifyResult;
1044  // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
1045  if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
1046  {
1047  // To get some reasonable response for point/line WMS vector layers we must
1048  // use a context with approximately a resolution in layer CRS units
1049  // corresponding to current map canvas resolution (for examplei UMN Mapserver
1050  // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
1051  // + TOLERANCE (layer param) for feature selection)
1052  //
1053  QgsRectangle r;
1054  r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
1055  r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
1056  r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
1057  r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
1058  r = toLayerCoordinates( layer, r ); // will be a bit larger
1059  // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
1060  // but that is fixed (the rect is enlarged) in the WMS provider
1061  identifyResult = dprovider->identify( point, format, r, 1, 1 );
1062  }
1063  else
1064  {
1065  // It would be nice to use the same extent and size which was used for drawing,
1066  // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
1067  // is doing some tricks with extent and size to align raster to output which
1068  // would be difficult to replicate here.
1069  // Note: cutting the extent may result in slightly different x and y resolutions
1070  // and thus shifted point calculated back in QGIS WMS (using average resolution)
1071  //viewExtent = dprovider->extent().intersect( &viewExtent );
1072 
1073  // Width and height are calculated from not projected extent and we hope that
1074  // are similar to source width and height used to reproject layer for drawing.
1075  // TODO: may be very dangerous, because it may result in different resolutions
1076  // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
1077  int width = static_cast< int >( std::round( viewExtent.width() / mapUnitsPerPixel ) );
1078  int height = static_cast< int >( std::round( viewExtent.height() / mapUnitsPerPixel ) );
1079 
1080  QgsDebugMsgLevel( QStringLiteral( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ), 2 );
1081  QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( width ).arg( height ), 2 );
1082  QgsDebugMsgLevel( QStringLiteral( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ), 2 );
1083 
1084  identifyResult = dprovider->identify( point, format, viewExtent, width, height );
1085  }
1086 
1087  QgsRasterLayerElevationProperties *elevationProperties = qobject_cast< QgsRasterLayerElevationProperties *>( layer->elevationProperties() );
1088  if ( identifyResult.isValid() && !identifyContext.zRange().isInfinite() && elevationProperties && elevationProperties->isEnabled() )
1089  {
1090  // filter results by z range
1091  switch ( format )
1092  {
1094  {
1095  bool foundMatch = false;
1096  QMap<int, QVariant> values = identifyResult.results();
1097  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1098  {
1099  if ( QgsVariantUtils::isNull( it.value() ) )
1100  {
1101  continue;
1102  }
1103  const double value = it.value().toDouble();
1104  const QgsDoubleRange elevationRange = elevationProperties->elevationRangeForPixelValue( it.key(), value );
1105  if ( !elevationRange.isInfinite() && identifyContext.zRange().overlaps( elevationRange ) )
1106  {
1107  foundMatch = true;
1108  break;
1109  }
1110  }
1111 
1112  if ( !foundMatch )
1113  return false;
1114 
1115  break;
1116  }
1117 
1118  // can't filter by z for these formats
1123  break;
1124  }
1125  }
1126 
1127  derivedAttributes.insert( derivedAttributesForPoint( QgsPoint( pointInCanvasCrs ) ) );
1128 
1129  const double xres = layer->rasterUnitsPerPixelX();
1130  const double yres = layer->rasterUnitsPerPixelY();
1131  QgsRectangle pixelRect;
1132  // Don't derive clicked column/row for providers that serve dynamically rendered map images
1133  if ( ( dprovider->capabilities() & QgsRasterDataProvider::Size ) && !qgsDoubleNear( xres, 0 ) && !qgsDoubleNear( yres, 0 ) )
1134  {
1135  // Try to determine the clicked column/row (0-based) in the raster
1136  const QgsRectangle extent = dprovider->extent();
1137 
1138  const int rasterCol = static_cast< int >( std::floor( ( point.x() - extent.xMinimum() ) / xres ) );
1139  const int rasterRow = static_cast< int >( std::floor( ( extent.yMaximum() - point.y() ) / yres ) );
1140 
1141  derivedAttributes.insert( tr( "Column (0-based)" ), QLocale().toString( rasterCol ) );
1142  derivedAttributes.insert( tr( "Row (0-based)" ), QLocale().toString( rasterRow ) );
1143 
1144  pixelRect = QgsRectangle( rasterCol * xres + extent.xMinimum(),
1145  extent.yMaximum() - ( rasterRow + 1 ) * yres,
1146  ( rasterCol + 1 ) * xres + extent.xMinimum(),
1147  extent.yMaximum() - ( rasterRow * yres ) );
1148  }
1149 
1150  if ( identifyResult.isValid() )
1151  {
1152  QMap<int, QVariant> values = identifyResult.results();
1153  if ( format == Qgis::RasterIdentifyFormat::Value )
1154  {
1155  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1156  {
1157  QString valueString;
1158  if ( QgsVariantUtils::isNull( it.value() ) )
1159  {
1160  valueString = tr( "no data" );
1161  }
1162  else
1163  {
1164  QVariant value( it.value() );
1165  // The cast is legit. Quoting QT doc :
1166  // "Although this function is declared as returning QVariant::Type,
1167  // the return value should be interpreted as QMetaType::Type"
1168  if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float )
1169  {
1170  valueString = QgsRasterBlock::printValue( value.toFloat() );
1171  }
1172  else
1173  {
1174  valueString = QgsRasterBlock::printValue( value.toDouble() );
1175  }
1176  }
1177  attributes.insert( dprovider->generateBandName( it.key() ), valueString );
1178 
1179  // Get raster attribute table attributes
1180  if ( const QgsRasterAttributeTable *rat = layer->attributeTable( it.key() ) )
1181  {
1182  bool ok;
1183  const double doubleValue { it.value().toDouble( &ok ) };
1184  if ( ok )
1185  {
1186  const QVariantList row = rat->row( doubleValue );
1187  if ( ! row.isEmpty() )
1188  {
1189  for ( int colIdx = 0; colIdx < std::min( rat->fields().count( ), row.count() ); ++colIdx )
1190  {
1191  const QgsRasterAttributeTable::Field ratField { rat->fields().at( colIdx ) };
1192 
1193  // Skip value and color fields
1194  if ( QgsRasterAttributeTable::valueAndColorFieldUsages().contains( ratField.usage ) )
1195  {
1196  continue;
1197  }
1198 
1199  QString ratValue;
1200  switch ( ratField.type )
1201  {
1202  case QVariant::Type::Char:
1203  case QVariant::Type::Int:
1204  case QVariant::Type::UInt:
1205  case QVariant::Type::LongLong:
1206  case QVariant::Type::ULongLong:
1207  ratValue = QLocale().toString( row.at( colIdx ).toLongLong() );
1208  break;
1209  case QVariant::Type::Double:
1210  ratValue = QLocale().toString( row.at( colIdx ).toDouble( ) );
1211  break;
1212  default:
1213  ratValue = row.at( colIdx ).toString();
1214  }
1215  attributes.insert( ratField.name, ratValue );
1216  }
1217  }
1218  }
1219  } // end RAT
1220 
1221  }
1222 
1223  QString label = layer->name();
1224  QgsFeature feature;
1225  if ( !pixelRect.isNull() )
1226  {
1227  feature.setGeometry( QgsGeometry::fromRect( pixelRect ) );
1228  }
1229 
1230  IdentifyResult result( qobject_cast<QgsMapLayer *>( layer ), label, QgsFields(), feature, derivedAttributes );
1231  result.mAttributes = attributes;
1232  results->append( result );
1233  }
1234  else if ( format == Qgis::RasterIdentifyFormat::Feature )
1235  {
1236  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1237  {
1238  QVariant value = it.value();
1239  if ( value.type() == QVariant::Bool && !value.toBool() )
1240  {
1241  // sublayer not visible or not queryable
1242  continue;
1243  }
1244 
1245  if ( value.type() == QVariant::String )
1246  {
1247  // error
1248  // TODO: better error reporting
1249  QString label = layer->subLayers().value( it.key() );
1250  attributes.clear();
1251  attributes.insert( tr( "Error" ), value.toString() );
1252 
1253  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1254  continue;
1255  }
1256 
1257  // list of feature stores for a single sublayer
1258  const QgsFeatureStoreList featureStoreList = value.value<QgsFeatureStoreList>();
1259 
1260  for ( const QgsFeatureStore &featureStore : featureStoreList )
1261  {
1262  const QgsFeatureList storeFeatures = featureStore.features();
1263  for ( const QgsFeature &feature : storeFeatures )
1264  {
1265  attributes.clear();
1266  // WMS sublayer and feature type, a sublayer may contain multiple feature types.
1267  // Sublayer name may be the same as layer name and feature type name
1268  // may be the same as sublayer. We try to avoid duplicities in label.
1269  QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
1270  QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
1271  // Strip UMN MapServer '_feature'
1272  featureType.remove( QStringLiteral( "_feature" ) );
1273  QStringList labels;
1274  if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
1275  {
1276  labels << sublayer;
1277  }
1278  if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
1279  {
1280  labels << featureType;
1281  }
1282 
1283  QMap< QString, QString > derAttributes = derivedAttributes;
1284  derAttributes.insert( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
1285 
1286  IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QLatin1String( " / " ) ), featureStore.fields(), feature, derAttributes );
1287 
1288  identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
1289  results->append( identifyResult );
1290  }
1291  }
1292  }
1293  }
1294  else // text or html
1295  {
1296  QgsDebugMsgLevel( QStringLiteral( "%1 HTML or text values" ).arg( values.size() ), 2 );
1297  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1298  {
1299  QString value = it.value().toString();
1300  attributes.clear();
1301  attributes.insert( QString(), value );
1302 
1303  QString label = layer->subLayers().value( it.key() );
1304  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1305  }
1306  }
1307  }
1308  else
1309  {
1310  attributes.clear();
1311  QString value = identifyResult.error().message( QgsErrorMessage::Text );
1312  attributes.insert( tr( "Error" ), value );
1313  QString label = tr( "Identify error" );
1314  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1315  }
1316 
1317  return true;
1318 }
1319 
1320 Qgis::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
1321 {
1322  return mCanvas->mapUnits();
1323 }
1324 
1325 Qgis::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
1326 {
1327  return QgsUnitTypes::distanceToAreaUnit( mCanvas->mapUnits() );
1328 }
1329 
1330 QString QgsMapToolIdentify::formatDistance( double distance ) const
1331 {
1332  return formatDistance( distance, displayDistanceUnits() );
1333 }
1334 
1335 QString QgsMapToolIdentify::formatArea( double area ) const
1336 {
1337  return formatArea( area, displayAreaUnits() );
1338 }
1339 
1340 QString QgsMapToolIdentify::formatDistance( double distance, Qgis::DistanceUnit unit ) const
1341 {
1342  QgsSettings settings;
1343  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1344 
1345  return QgsDistanceArea::formatDistance( distance, mCoordinatePrecision, unit, baseUnit );
1346 }
1347 
1348 QString QgsMapToolIdentify::formatArea( double area, Qgis::AreaUnit unit ) const
1349 {
1350  QgsSettings settings;
1351  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1352 
1353  return QgsDistanceArea::formatArea( area, mCoordinatePrecision, unit, baseUnit );
1354 }
1355 
1357 {
1358  QList<IdentifyResult> results;
1359  if ( identifyRasterLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel ) )
1360  {
1361  emit changedRasterResults( results );
1362  }
1363 }
1364 
1365 void QgsMapToolIdentify::fromPointCloudIdentificationToIdentifyResults( QgsPointCloudLayer *layer, const QVector<QVariantMap> &identified, QList<QgsMapToolIdentify::IdentifyResult> &results )
1366 {
1367  int id = 1;
1368  const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
1369  for ( const QVariantMap &pt : identified )
1370  {
1371  QMap<QString, QString> ptStr;
1372  QString classification;
1373  for ( auto attrIt = pt.constBegin(); attrIt != pt.constEnd(); ++attrIt )
1374  {
1375  if ( attrIt.key().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) == 0
1376  && ( !qgsDoubleNear( elevationProps->zScale(), 1 ) || !qgsDoubleNear( elevationProps->zOffset(), 0 ) ) )
1377  {
1378  // Apply elevation properties
1379  ptStr[ tr( "Z (original)" ) ] = attrIt.value().toString();
1380  ptStr[ tr( "Z (adjusted)" ) ] = QString::number( attrIt.value().toDouble() * elevationProps->zScale() + elevationProps->zOffset() );
1381  }
1382  else if ( attrIt.key().compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
1383  {
1384  classification = QgsPointCloudDataProvider::translatedLasClassificationCodes().value( attrIt.value().toInt() );
1385  ptStr[ attrIt.key() ] = QStringLiteral( "%1 (%2)" ).arg( attrIt.value().toString(), classification );
1386  }
1387  else
1388  {
1389  ptStr[attrIt.key()] = attrIt.value().toString();
1390  }
1391  }
1392  QgsMapToolIdentify::IdentifyResult res( layer, classification.isEmpty() ? QString::number( id ) : QStringLiteral( "%1 (%2)" ).arg( id ).arg( classification ), ptStr, QMap<QString, QString>() );
1393  results.append( res );
1394  ++id;
1395  }
1396 }
1397 
1398 void QgsMapToolIdentify::fromElevationProfileLayerIdentificationToIdentifyResults( QgsMapLayer *layer, const QVector<QVariantMap> &identified, QList<IdentifyResult> &results )
1399 {
1400  if ( !layer )
1401  return;
1402 
1403  if ( identified.empty() )
1404  return;
1405 
1406  switch ( layer->type() )
1407  {
1409  {
1410  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
1411 
1412  QgsFeatureList features;
1413  QHash< QgsFeatureId, QVariant > featureDistances;
1414  QHash< QgsFeatureId, QVariant > featureElevations;
1415 
1416  QgsFeatureIds filterIds;
1417  for ( const QVariantMap &map : identified )
1418  {
1419  if ( !map.contains( QStringLiteral( "id" ) ) )
1420  {
1421  QMap< QString, QString > attributes;
1422  if ( map.value( QStringLiteral( "distance" ) ).isValid() )
1423  attributes.insert( tr( "Distance along curve" ), QString::number( map.value( QStringLiteral( "distance" ) ).toDouble() ) );
1424  if ( map.value( QStringLiteral( "elevation" ) ).isValid() )
1425  attributes.insert( tr( "Elevation" ), QString::number( map.value( QStringLiteral( "elevation" ) ).toDouble() ) );
1426 
1427  results.append( IdentifyResult( layer, layer->name(), {}, attributes ) );
1428  }
1429  else
1430  {
1431  const QgsFeatureId id = map.value( QStringLiteral( "id" ) ).toLongLong();
1432  filterIds.insert( id );
1433 
1434  featureDistances.insert( id, map.value( QStringLiteral( "distance" ) ) );
1435  featureElevations.insert( id, map.value( QStringLiteral( "elevation" ) ) );
1436  }
1437  }
1438 
1439  QgsFeatureRequest request;
1440  request.setFilterFids( filterIds );
1441  QgsFeatureIterator it = vl->getFeatures( request );
1442  QgsFeature f;
1443  while ( it.nextFeature( f ) )
1444  features << f;
1445 
1446  QgsRenderContext context;
1447  identifyVectorLayer( &results, vl, features, nullptr, QMap< QString, QString >(), [this, vl, &featureDistances, &featureElevations]( const QgsFeature & feature )->QMap< QString, QString >
1448  {
1449  QMap< QString, QString > attributes = featureDerivedAttributes( feature, vl, QgsPointXY() );
1450 
1451  if ( featureDistances.value( feature.id() ).isValid() )
1452  attributes.insert( tr( "Distance along curve" ), QString::number( featureDistances.value( feature.id() ).toDouble() ) );
1453  if ( featureElevations.value( feature.id() ).isValid() )
1454  attributes.insert( tr( "Elevation" ), QString::number( featureElevations.value( feature.id() ).toDouble() ) );
1455 
1456  return attributes;
1457  }, context );
1458  break;
1459  }
1460 
1462  case Qgis::LayerType::Mesh:
1463  {
1464  for ( const QVariantMap &map : identified )
1465  {
1466  QMap< QString, QString > attributes;
1467  if ( map.value( QStringLiteral( "distance" ) ).isValid() )
1468  attributes.insert( tr( "Distance along curve" ), QString::number( map.value( QStringLiteral( "distance" ) ).toDouble() ) );
1469  if ( map.value( QStringLiteral( "elevation" ) ).isValid() )
1470  attributes.insert( tr( "Elevation" ), QString::number( map.value( QStringLiteral( "elevation" ) ).toDouble() ) );
1471 
1472  results.append( IdentifyResult( layer, layer->name(), {}, attributes ) );
1473  }
1474 
1475  break;
1476  }
1477 
1479  {
1480  QgsPointCloudLayer *pcLayer = qobject_cast< QgsPointCloudLayer * >( layer );
1481  fromPointCloudIdentificationToIdentifyResults( pcLayer, identified, results );
1482  break;
1483  }
1484 
1490  break;
1491  }
1492 }
DistanceUnit
Units of distance.
Definition: qgis.h:4090
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ EmbeddedSymbols
Retrieve any embedded feature symbology (since QGIS 3.20)
@ Curve
An intermediate point on a segment defining the curvature of the segment.
AreaUnit
Units of area.
Definition: qgis.h:4128
QFlags< FeatureRequestFlag > FeatureRequestFlags
Flags for controlling feature requests.
Definition: qgis.h:1732
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:255
@ Polygon
Polygons.
@ Null
No geometry.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
RasterIdentifyFormat
Raster identify formats.
Definition: qgis.h:3977
@ Feature
WMS GML/JSON -> feature.
@ Value
Numerical pixel value.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ LineString
LineString.
@ NoGeometry
No geometry.
Abstract base class for all geometries.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual double perimeter() const
Returns the planar, 2-dimensional perimeter of the geometry.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary const part after the last part of the geometry.
const_part_iterator const_parts_begin() const
Returns STL-style iterator pointing to the const first part of the geometry.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
Q_GADGET Qgis::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
static QString formatDistance(double distance, int decimals, Qgis::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
static QString formatArea(double area, int decimals, Qgis::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
QgsRange which stores a range of double values.
Definition: qgsrange.h:231
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition: qgsrange.h:285
QString message(QgsErrorMessage::Format format=QgsErrorMessage::Html) const
Full error messages description.
Definition: qgserror.cpp:49
QString what() const
Definition: qgsexception.h:49
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.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Definition: qgsrenderer.h:272
virtual bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns whether the renderer will render a feature or not.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
A container for features with the same fields and crs.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Definition: qgsfeature.cpp:321
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:167
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
Geometry collection.
int numGeometries() const
Returns the number of geometries within the collection.
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
Qgis::GeometryType type
Definition: qgsgeometry.h:165
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
Identify contexts are used to encapsulate the settings to be used to perform an identify action.
bool isTemporal() const
Returns true if the temporal range setting is enabled.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range to be used with the identify action.
QgsDoubleRange zRange() const
Returns the range of z-values to identify within, or an infinite range if no filtering by z should be...
The QgsIdentifyMenu class builds a menu to be used with identify results (.
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:93
double zScale() const
Returns the z scale, which is a scaling factor which should be applied to z values from the layer.
virtual bool isVisibleInZRange(const QgsDoubleRange &range) const
Returns true if the layer should be visible and rendered for the specified z range.
double zOffset() const
Returns the z offset, which is a fixed offset amount which should be added to z values from the layer...
virtual bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const
Returns true if the layer should be visible and rendered for the specified time range.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
QString name
Definition: qgsmaplayer.h:78
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
virtual QgsRectangle extent() const
Returns the extent of the layer.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
Qgis::LayerType type
Definition: qgsmaplayer.h:82
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
virtual QStringList subLayers() const
Returns the sublayers of this layer.
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:151
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Definition: qgsmaplayer.h:1633
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1626
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
void fromElevationProfileLayerIdentificationToIdentifyResults(QgsMapLayer *layer, const QVector< QVariantMap > &identified, QList< QgsMapToolIdentify::IdentifyResult > &results)
Converts elevation profile identification results from variant maps to QgsMapToolIdentify::IdentifyRe...
QFlags< Type > LayerType
QMap< QString, QString > derivedAttributesForPoint(const QgsPoint &point)
Returns derived attributes map for a clicked point in map coordinates. May be 2D or 3D point.
bool identifyLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType=AllLayers, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Call the right method depending on layer type.
static void fromPointCloudIdentificationToIdentifyResults(QgsPointCloudLayer *layer, const QVector< QVariantMap > &identified, QList< QgsMapToolIdentify::IdentifyResult > &results)
Converts point cloud identification results from variant maps to QgsMapToolIdentify::IdentifyResult a...
void changedRasterResults(QList< QgsMapToolIdentify::IdentifyResult > &)
void identifyProgress(int, int)
void deactivate() override
called when map tool is being deactivated
void identifyMessage(const QString &)
void activate() override
called when set as currently active map tool
QList< QgsMapToolIdentify::IdentifyResult > identify(int x, int y, const QList< QgsMapLayer * > &layerList=QList< QgsMapLayer * >(), IdentifyMode mode=DefaultQgsSetting, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification.
void formatChanged(QgsRasterLayer *layer)
void canvasReleaseEvent(QgsMapMouseEvent *e) override
Mouse release event for overriding. Default implementation does nothing.
void setCanvasPropertiesOverrides(double searchRadiusMapUnits)
Overrides some map canvas properties inside the map tool for the upcoming identify requests.
QgsMapToolIdentify(QgsMapCanvas *canvas)
constructor
void canvasMoveEvent(QgsMapMouseEvent *e) override
Mouse move event for overriding. Default implementation does nothing.
bool identifyRasterLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification against a given raster layer.
bool identifyMeshLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsMeshLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Identifies data from active scalar and vector dataset from the mesh layer.
QgsIdentifyMenu * mIdentifyMenu
void canvasPressEvent(QgsMapMouseEvent *e) override
Mouse press event for overriding. Default implementation does nothing.
bool identifyVectorLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsVectorLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification against a given vector layer.
void restoreCanvasPropertiesOverrides()
Clears canvas properties overrides previously set with setCanvasPropertiesOverrides()
Abstract base class for all map tools.
Definition: qgsmaptool.h:71
QgsPoint toLayerCoordinates(const QgsMapLayer *layer, const QgsPoint &point)
Transforms a point from map coordinates to layer coordinates.
Definition: qgsmaptool.cpp:62
QPointer< QgsMapCanvas > mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:341
QgsMapLayer * layer(const QString &id)
Returns the map layer with the matching ID, or nullptr if no layers could be found.
Definition: qgsmaptool.cpp:84
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
Definition: qgsmaptool.cpp:41
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
Definition: qgsmaptool.cpp:166
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:238
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:77
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:94
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:110
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isTemporal() const
Returns whether the dataset group is temporal (contains time-related dataset)
bool isVector() const
Returns whether dataset group has vector data.
QString name() const
Returns name of the dataset group.
bool isScalar() const
Returns whether dataset group has scalar data.
QString uri() const
Returns the uri of the source.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
QgsMeshDatasetMetadata is a collection of mesh dataset metadata such as whether the data is valid or ...
double time() const
Returns the time value for this dataset.
QgsMeshDatasetValue represents single dataset value.
double y() const
Returns y value.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
double x() const
Returns x value.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:101
static QMap< int, QString > translatedLasClassificationCodes()
Returns the map of LAS classification code to translated string value, corresponding to the ASPRS Sta...
Point cloud layer specific subclass of QgsMapLayerElevationProperties.
Represents a map layer supporting display of point clouds.
Abstract base class for 2d point cloud renderers.
QVector< QVariantMap > identify(QgsPointCloudLayer *layer, const QgsRenderContext &context, const QgsGeometry &geometry, double toleranceForPointIdentification=0)
Returns the list of visible points of the point cloud layer layer and an extent defined by a geometry...
virtual void startRender(QgsPointCloudRenderContext &context)
Must be called when a new render cycle is started.
virtual void stopRender(QgsPointCloudRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
A class to represent a 2D point.
Definition: qgspointxy.h:60
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Definition: qgspointxy.cpp:51
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition: qgspointxy.h:243
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
QString ellipsoid
Definition: qgsproject.h:114
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:113
bool overlaps(const QgsRange< T > &other) const
Returns true if this range overlaps another range.
Definition: qgsrange.h:176
The Field class represents a Raster Attribute Table field, including its name, usage and type.
The QgsRasterAttributeTable class represents a Raster Attribute Table (RAT).
static QList< Qgis::RasterAttributeTableFieldUsage > valueAndColorFieldUsages()
Returns the list of field usages for colors and values.
static QString printValue(double value)
Print double value with all necessary significant digits.
static Capability identifyFormatToCapability(Qgis::RasterIdentifyFormat format)
Converts a raster identify format to a capability.
static Qgis::RasterIdentifyFormat identifyFormatFromName(const QString &formatName)
Converts a string formatName to a raster identify format.
Raster identify results container.
QgsError error() const
Returns the last error.
bool isValid() const
Returns true if valid.
QMap< int, QVariant > results() const
Returns the identify results.
@ IdentifyValue
Numerical values.
@ Identify
At least one identify format supported.
@ IdentifyFeature
WMS GML -> feature.
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Raster layer specific subclass of QgsMapLayerElevationProperties.
bool isEnabled() const
Returns true if the elevation properties are enabled, i.e.
QgsDoubleRange elevationRangeForPixelValue(int band, double pixelValue) const
Returns the elevation range corresponding to a raw pixel value from the specified band.
Represents a raster layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:385
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:159
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:149
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
bool isNull() const
Test if the rectangle is null (holding no spatial information).
Definition: qgsrectangle.h:505
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:164
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:154
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:243
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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setZRange(const QgsDoubleRange &range)
Sets the range of z-values which should be rendered.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
Definition: qgssettings.h:263
static QString symbolTypeToString(Qgis::SymbolType type)
Returns a translated string version of the specified symbol type.
Definition: qgssymbol.cpp:565
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:912
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:156
bool isActive() const
Returns true if the temporal property is active.
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:255
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:134
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:97
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:97
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
Helper functions for various unit types.
Definition: qgsunittypes.h:39
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE Qgis::DistanceUnitType unitType(Qgis::DistanceUnit unit)
Returns the type for a distance unit.
static Q_INVOKABLE Qgis::AreaUnit distanceToAreaUnit(Qgis::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
@ FeatureSymbology
Provider is able retrieve embedded symbology associated with individual features. Since QGIS 3....
Encapsulates the context in which a QgsVectorLayer's temporal capabilities will be applied.
void setLayer(QgsVectorLayer *layer)
Sets the associated layer.
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.
Implements a map layer that is dedicated to rendering of vector tiles.
This class is responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
Keeps track of raw tile data that need to be decoded.
QByteArray data
Raw tile data.
static QgsFields makeQgisFields(const QSet< QString > &flds)
Returns QgsFields instance based on the set of field names.
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:758
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition: qgswkbtypes.h:53
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
#define str(x)
Definition: qgis.cpp:38
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:5659
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5172
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:917
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
#define FID_TO_STRING(fid)
Definition: qgsfeatureid.h:33
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QVector< QgsFeatureStore > QgsFeatureStoreList
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)
const QgsCoordinateReferenceSystem & crs
QMap< QString, QString > mAttributes
QMap< QString, QVariant > mParams
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:30
int vertex
Vertex number.
Definition: qgsvertexid.h:94
bool isValid() const
Returns true if the vertex id is valid.
Definition: qgsvertexid.h:45
int part
Part number.
Definition: qgsvertexid.h:88
Qgis::VertexType type
Vertex type.
Definition: qgsvertexid.h:97