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