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