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