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