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