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