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