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