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