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