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