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