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