QGIS API Documentation 3.41.0-Master (cea29feecf2)
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, resultName, attribute, derivedAttributes );
373
374 results->append( result );
375 }
376
377 QMap<QString, QString> derivedGeometry;
378
379 QgsPointXY vertexPoint = layer->snapOnElement( QgsMesh::Vertex, point, searchRadius );
380 if ( !vertexPoint.isEmpty() )
381 {
382 derivedGeometry.insert( tr( "Snapped Vertex Position X" ), QLocale().toString( vertexPoint.x() ) );
383 derivedGeometry.insert( tr( "Snapped Vertex Position Y" ), QLocale().toString( vertexPoint.y() ) );
384 }
385
386 QgsPointXY faceCentroid = layer->snapOnElement( QgsMesh::Face, point, searchRadius );
387 if ( !faceCentroid.isEmpty() )
388 {
389 derivedGeometry.insert( tr( "Face Centroid X" ), QLocale().toString( faceCentroid.x() ) );
390 derivedGeometry.insert( tr( "Face Centroid Y" ), QLocale().toString( faceCentroid.y() ) );
391 }
392
393 QgsPointXY pointOnEdge = layer->snapOnElement( QgsMesh::Edge, point, searchRadius );
394 if ( !pointOnEdge.isEmpty() )
395 {
396 derivedGeometry.insert( tr( "Point on Edge X" ), QLocale().toString( pointOnEdge.x() ) );
397 derivedGeometry.insert( tr( "Point on Edge Y" ), QLocale().toString( pointOnEdge.y() ) );
398 }
399
400 const IdentifyResult result( layer, tr( "Geometry" ), derivedAttributesForPoint( QgsPoint( point ) ), derivedGeometry );
401
402 results->append( result );
403
404 return true;
405}
406
407bool QgsMapToolIdentify::identifyVectorTileLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorTileLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
408{
409 Q_UNUSED( identifyContext )
410 if ( !layer || !layer->isSpatial() )
411 return false;
412
413 if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
414 {
415 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
416 return false;
417 }
418
419 QgsTemporaryCursorOverride waitCursor( Qt::WaitCursor );
420
421 QMap<QString, QString> commonDerivedAttributes;
422
423 QgsGeometry selectionGeom = geometry;
424 bool isPointOrRectangle;
425 QgsPointXY point;
426 bool isSingleClick = selectionGeom.type() == Qgis::GeometryType::Point;
427 if ( isSingleClick )
428 {
429 isPointOrRectangle = true;
430 point = selectionGeom.asPoint();
431
432 commonDerivedAttributes = derivedAttributesForPoint( QgsPoint( point ) );
433 }
434 else
435 {
436 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
437 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
438 }
439
440 int featureCount = 0;
441
442 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
443
444 // toLayerCoordinates will throw an exception for an 'invalid' point.
445 // For example, if you project a world map onto a globe using EPSG 2163
446 // and then click somewhere off the globe, an exception will be thrown.
447 try
448 {
449 QgsRectangle r;
450 if ( isSingleClick )
451 {
452 double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
453 r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
454 }
455 else
456 {
457 r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
458
459 if ( !isPointOrRectangle )
460 {
461 QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), layer->crs(), mCanvas->mapSettings().transformContext() );
462 if ( ct.isValid() )
463 selectionGeom.transform( ct );
464
465 // use prepared geometry for faster intersection test
466 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
467 }
468 }
469
470 const double tileScale = layer->tileMatrixSet().calculateTileScaleForMap(
471 mCanvas->scale(),
472 mCanvas->mapSettings().destinationCrs(),
473 mCanvas->mapSettings().extent(),
474 mCanvas->size(),
475 mCanvas->logicalDpiX()
476 );
477
478 const int tileZoom = layer->tileMatrixSet().scaleToZoomLevel( tileScale );
479 const QgsTileMatrix tileMatrix = layer->tileMatrixSet().tileMatrix( tileZoom );
480 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
481
482 const QVector<QgsTileXYZ> tiles = layer->tileMatrixSet().tilesInRange( tileRange, tileZoom );
483
484 for ( const QgsTileXYZ &tileID : tiles )
485 {
486 const QgsVectorTileRawData data = layer->getRawTile( tileID );
487 if ( data.data.isEmpty() )
488 continue; // failed to get data
489
490 QgsVectorTileMVTDecoder decoder( layer->tileMatrixSet() );
491 if ( !decoder.decode( data ) )
492 continue; // failed to decode
493
494 QMap<QString, QgsFields> perLayerFields;
495 const QStringList layerNames = decoder.layers();
496 for ( const QString &layerName : layerNames )
497 {
498 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
499 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
500 }
501
502 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
503 const QStringList featuresLayerNames = features.keys();
504 for ( const QString &layerName : featuresLayerNames )
505 {
506 const QgsFields fFields = perLayerFields[layerName];
507 const QVector<QgsFeature> &layerFeatures = features[layerName];
508 for ( const QgsFeature &f : layerFeatures )
509 {
510 if ( f.geometry().intersects( r ) && ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) ) )
511 {
512 QMap<QString, QString> derivedAttributes = commonDerivedAttributes;
513 derivedAttributes.insert( tr( "Feature ID" ), FID_TO_STRING( f.id() ) );
514 derivedAttributes.insert( tr( "Tile column" ), QString::number( tileID.column() ) );
515 derivedAttributes.insert( tr( "Tile row" ), QString::number( tileID.row() ) );
516 derivedAttributes.insert( tr( "Tile zoom" ), QString::number( tileID.zoomLevel() ) );
517
518 results->append( IdentifyResult( layer, layerName, fFields, f, derivedAttributes ) );
519
520 featureCount++;
521 }
522 }
523 }
524 }
525 }
526 catch ( QgsCsException &cse )
527 {
528 Q_UNUSED( cse )
529 // catch exception for 'invalid' point and proceed with no features found
530 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
531 }
532
533 return featureCount > 0;
534}
535
536bool QgsMapToolIdentify::identifyPointCloudLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsPointCloudLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
537{
538 if ( !identifyContext.zRange().isInfinite() )
539 {
540 if ( !layer->elevationProperties()->isVisibleInZRange( identifyContext.zRange(), layer ) )
541 return false;
542 }
543
544 QgsPointCloudRenderer *renderer = layer->renderer();
545
547 context.setCoordinateTransform( QgsCoordinateTransform( layer->crs(), mCanvas->mapSettings().destinationCrs(), mCanvas->mapSettings().transformContext() ) );
548 if ( !identifyContext.zRange().isInfinite() )
549 context.setZRange( identifyContext.zRange() );
550
551 const double searchRadiusMapUnits = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
552
553 const QVector<QVariantMap> points = renderer->identify( layer, context, geometry, searchRadiusMapUnits );
554
556
557 return true;
558}
559
560QMap<QString, QString> QgsMapToolIdentify::derivedAttributesForPoint( const QgsPoint &point )
561{
562 QMap<QString, QString> derivedAttributes;
563
564 QString x;
565 QString y;
566 formatCoordinate( point, x, y );
567
568 derivedAttributes.insert( tr( "(clicked coordinate X)" ), x );
569 derivedAttributes.insert( tr( "(clicked coordinate Y)" ), y );
570 if ( point.is3D() )
571 derivedAttributes.insert( tr( "(clicked coordinate Z)" ), QLocale().toString( point.z(), 'f' ) );
572 return derivedAttributes;
573}
574
575bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
576{
577 if ( !layer || !layer->isSpatial() || !layer->dataProvider() )
578 return false;
579
580 if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
581 {
582 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
583 return false;
584 }
585
586 QString temporalFilter;
587 if ( identifyContext.isTemporal() )
588 {
589 if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
590 return false;
591
592 QgsVectorLayerTemporalContext temporalContext;
593 temporalContext.setLayer( layer );
594 temporalFilter = qobject_cast<const QgsVectorLayerTemporalProperties *>( layer->temporalProperties() )->createFilterString( temporalContext, identifyContext.temporalRange() );
595 }
596
597 const bool fetchFeatureSymbols = layer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::FeatureSymbology;
598
599 QApplication::setOverrideCursor( Qt::WaitCursor );
600
601 QMap<QString, QString> commonDerivedAttributes;
602
603 QgsGeometry selectionGeom = geometry;
604 bool isPointOrRectangle;
605 QgsPoint point;
606 bool isSingleClick = selectionGeom.type() == Qgis::GeometryType::Point;
607 if ( isSingleClick )
608 {
609 isPointOrRectangle = true;
610 point = *qgsgeometry_cast<const QgsPoint *>( selectionGeom.constGet() );
611
612 commonDerivedAttributes = derivedAttributesForPoint( point );
613 }
614 else
615 {
616 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
617 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
618 }
619
620 QgsFeatureList featureList;
621 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
622
623 // toLayerCoordinates will throw an exception for an 'invalid' point.
624 // For example, if you project a world map onto a globe using EPSG 2163
625 // and then click somewhere off the globe, an exception will be thrown.
626 try
627 {
628 QgsRectangle r;
629 if ( isSingleClick )
630 {
631 double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
632 r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
633 }
634 else
635 {
636 r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
637
638 if ( !isPointOrRectangle )
639 {
640 QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), layer->crs(), mCanvas->mapSettings().transformContext() );
641 if ( ct.isValid() )
642 selectionGeom.transform( ct );
643
644 // use prepared geometry for faster intersection test
645 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
646 }
647 }
648
649 QgsFeatureRequest featureRequest;
650 featureRequest.setFilterRect( r );
651 featureRequest.setFlags( Qgis::FeatureRequestFlag::ExactIntersect | ( fetchFeatureSymbols ? Qgis::FeatureRequestFlag::EmbeddedSymbols : Qgis::FeatureRequestFlags() ) );
652 if ( !temporalFilter.isEmpty() )
653 featureRequest.setFilterExpression( temporalFilter );
654
655 QgsFeatureIterator fit = layer->getFeatures( featureRequest );
656 QgsFeature f;
657 while ( fit.nextFeature( f ) )
658 {
659 if ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) )
660 featureList << QgsFeature( f );
661 }
662 }
663 catch ( QgsCsException &cse )
664 {
665 Q_UNUSED( cse )
666 // catch exception for 'invalid' point and proceed with no features found
667 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
668 }
669
670 bool filter = false;
671
673 context.setExpressionContext( mCanvas->createExpressionContext() );
675 std::unique_ptr<QgsFeatureRenderer> renderer( layer->renderer() ? layer->renderer()->clone() : nullptr );
676 if ( renderer )
677 {
678 // setup scale for scale dependent visibility (rule based)
679 renderer->startRender( context, layer->fields() );
680 filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
681 }
682
683 // When not single click identify, pass an empty point so some derived attributes may still be computed
684 if ( !isSingleClick )
685 point = QgsPoint();
686
687 const int featureCount = identifyVectorLayer( results, layer, featureList, filter ? renderer.get() : nullptr, commonDerivedAttributes, [point, layer, this]( const QgsFeature &feature ) -> QMap<QString, QString> { return featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ); }, context );
688
689 if ( renderer )
690 {
691 renderer->stopRender( context );
692 }
693 QApplication::restoreOverrideCursor();
694 return featureCount > 0;
695}
696
697int 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 )
698{
699 int featureCount = 0;
700 for ( const QgsFeature &feature : std::as_const( features ) )
701 {
702 QMap<QString, QString> derivedAttributes = commonDerivedAttributes;
703
704 QgsFeatureId fid = feature.id();
705 context.expressionContext().setFeature( feature );
706
707 if ( renderer && !renderer->willRenderFeature( feature, context ) )
708 continue;
709
710 derivedAttributes.insert( deriveAttributesForFeature( feature ) );
711 derivedAttributes.insert( tr( "Feature ID" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) );
712
713 results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), feature, derivedAttributes ) );
714 featureCount++;
715 }
716 return featureCount;
717}
718
719void QgsMapToolIdentify::closestVertexAttributes( const QgsCoordinateTransform layerToMapTransform, const QgsCoordinateReferenceSystem &layerVertCrs, const QgsCoordinateReferenceSystem &mapVertCrs, const QgsAbstractGeometry &geometry, QgsVertexId vId, bool showTransformedZ, QMap<QString, QString> &derivedAttributes )
720{
721 if ( !vId.isValid() )
722 {
723 // We should not get here ...
724 QgsDebugError( "Invalid vertex id!" );
725 return;
726 }
727
728 QString str = QLocale().toString( vId.vertex + 1 );
729 derivedAttributes.insert( tr( "Closest vertex number" ), str );
730
731 QgsPoint closestPoint = geometry.vertexAt( vId );
732 QgsPoint closestPointMapCoords = closestPoint;
733 if ( layerToMapTransform.isValid() )
734 {
735 try
736 {
737 closestPointMapCoords.transform( layerToMapTransform, Qgis::TransformDirection::Forward, layerToMapTransform.hasVerticalComponent() );
738 }
739 catch ( QgsCsException &cse )
740 {
741 QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
742 }
743 }
744
745 QString x;
746 QString y;
747 formatCoordinate( closestPointMapCoords, x, y );
748 derivedAttributes.insert( tr( "Closest vertex X" ), x );
749 derivedAttributes.insert( tr( "Closest vertex Y" ), y );
750
751 if ( closestPoint.is3D() )
752 {
753 str = QLocale().toString( closestPoint.z(), 'g', 10 );
754 derivedAttributes.insert( showTransformedZ ? tr( "Closest vertex Z (%1)" ).arg( layerVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ) : tr( "Closest vertex Z" ), str );
755 }
756 if ( showTransformedZ && !std::isnan( closestPointMapCoords.z() ) && !qgsDoubleNear( closestPoint.z(), closestPointMapCoords.z() ) )
757 {
758 const QString str = QLocale().toString( closestPointMapCoords.z(), 'g', 10 );
759 derivedAttributes.insert( tr( "Closest vertex Z (%1)" ).arg( mapVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ), str );
760 }
761
762 if ( closestPoint.isMeasure() )
763 {
764 str = QLocale().toString( closestPointMapCoords.m(), 'g', 10 );
765 derivedAttributes.insert( tr( "Closest vertex M" ), str );
766 }
767
768 if ( vId.type == Qgis::VertexType::Curve )
769 {
770 double radius, centerX, centerY;
771 QgsVertexId vIdBefore = vId;
772 --vIdBefore.vertex;
773 QgsVertexId vIdAfter = vId;
774 ++vIdAfter.vertex;
775 QgsGeometryUtils::circleCenterRadius( geometry.vertexAt( vIdBefore ), geometry.vertexAt( vId ), geometry.vertexAt( vIdAfter ), radius, centerX, centerY );
776 derivedAttributes.insert( QStringLiteral( "Closest vertex radius" ), QLocale().toString( radius ) );
777 }
778}
779
780void QgsMapToolIdentify::closestPointAttributes( const QgsCoordinateTransform layerToMapTransform, const QgsCoordinateReferenceSystem &layerVertCrs, const QgsCoordinateReferenceSystem &mapVertCrs, const QgsAbstractGeometry &geometry, const QgsPointXY &layerPoint, bool showTransformedZ, QMap<QString, QString> &derivedAttributes )
781{
782 QgsPoint closestPoint = QgsGeometryUtils::closestPoint( geometry, QgsPoint( layerPoint ) );
783 QgsPoint closestPointMapCrs = closestPoint;
784 if ( layerToMapTransform.isValid() )
785 {
786 try
787 {
788 closestPointMapCrs.transform( layerToMapTransform, Qgis::TransformDirection::Forward, layerToMapTransform.hasVerticalComponent() );
789 }
790 catch ( QgsCsException &cse )
791 {
792 QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
793 }
794 }
795
796 QString x;
797 QString y;
798 formatCoordinate( closestPoint, x, y );
799 derivedAttributes.insert( tr( "Closest X" ), x );
800 derivedAttributes.insert( tr( "Closest Y" ), y );
801
802 if ( closestPoint.is3D() )
803 {
804 const QString str = QLocale().toString( closestPoint.z(), 'g', 10 );
805 derivedAttributes.insert( showTransformedZ ? tr( "Interpolated Z (%1)" ).arg( layerVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ) : tr( "Interpolated Z" ), str );
806 }
807 if ( showTransformedZ && !std::isnan( closestPointMapCrs.z() ) && !qgsDoubleNear( closestPoint.z(), closestPointMapCrs.z() ) )
808 {
809 const QString str = QLocale().toString( closestPointMapCrs.z(), 'g', 10 );
810 derivedAttributes.insert( tr( "Interpolated Z (%1)" ).arg( mapVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ), str );
811 }
812
813 if ( closestPoint.isMeasure() )
814 {
815 const QString str = QLocale().toString( closestPoint.m(), 'g', 10 );
816 derivedAttributes.insert( tr( "Interpolated M" ), str );
817 }
818}
819
820void QgsMapToolIdentify::formatCoordinate( const QgsPointXY &canvasPoint, QString &x, QString &y, const QgsCoordinateReferenceSystem &mapCrs, int coordinatePrecision )
821{
822 QgsCoordinateUtils::formatCoordinatePartsForProject( QgsProject::instance(), canvasPoint, mapCrs, coordinatePrecision, x, y );
823}
824
825void QgsMapToolIdentify::formatCoordinate( const QgsPointXY &canvasPoint, QString &x, QString &y ) const
826{
827 formatCoordinate( canvasPoint, x, y, mCanvas->mapSettings().destinationCrs(), mCoordinatePrecision );
828}
829
830QMap<QString, QString> QgsMapToolIdentify::featureDerivedAttributes( const QgsFeature &feature, QgsMapLayer *layer, const QgsPointXY &layerPoint )
831{
832 // Calculate derived attributes and insert:
833 // measure distance or area depending on geometry type
834 QMap<QString, QString> derivedAttributes;
835
836 // init distance/area calculator
837 QString ellipsoid = QgsProject::instance()->ellipsoid();
838 QgsDistanceArea calc;
839 calc.setEllipsoid( ellipsoid );
841
844
845 QgsVertexId vId;
846 QgsPoint closestPoint;
847 if ( feature.hasGeometry() )
848 {
849 geometryType = feature.geometry().type();
850 wkbType = feature.geometry().wkbType();
851 if ( !layerPoint.isEmpty() )
852 {
853 //find closest vertex to clicked point
854 closestPoint = QgsGeometryUtils::closestVertex( *feature.geometry().constGet(), QgsPoint( layerPoint ), vId );
855 }
856 }
857
858 if ( QgsWkbTypes::isMultiType( wkbType ) )
859 {
860 QString str = QLocale().toString( static_cast<const QgsGeometryCollection *>( feature.geometry().constGet() )->numGeometries() );
861 derivedAttributes.insert( tr( "Parts" ), str );
862 if ( !layerPoint.isEmpty() )
863 {
864 str = QLocale().toString( vId.part + 1 );
865 derivedAttributes.insert( tr( "Part number" ), str );
866 }
867 }
868
869 Qgis::DistanceUnit cartesianDistanceUnits = QgsUnitTypes::unitType( layer->crs().mapUnits() ) == QgsUnitTypes::unitType( displayDistanceUnits() )
870 ? displayDistanceUnits()
871 : layer->crs().mapUnits();
873 ? displayAreaUnits()
874 : QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() );
875
879 : layer->crs3D();
880 const bool showTransformedZ = QgsProject::instance()->crs3D() != layer->crs3D() && QgsProject::instance()->crs3D().hasVerticalAxis() && layer->crs3D().hasVerticalAxis();
882
883 const QgsGeometry layerCrsGeometry = feature.geometry();
884 QgsGeometry mapCrsGeometry = layerCrsGeometry;
885 try
886 {
887 if ( layerToMapTransform.isValid() )
888 {
889 mapCrsGeometry.transform( layerToMapTransform, Qgis::TransformDirection::Forward, layerToMapTransform.hasVerticalComponent() );
890 }
891 }
892 catch ( QgsCsException &cse )
893 {
894 QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
895 }
896
897 if ( geometryType == Qgis::GeometryType::Line )
898 {
899 const QgsAbstractGeometry *layerCrsGeom = layerCrsGeometry.constGet();
900
901 double dist = 0;
902 try
903 {
904 dist = calc.measureLength( feature.geometry() );
905 dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
906 }
907 catch ( QgsCsException & )
908 {
909 //TODO report errors to user
910 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
911 }
912
913 QString str;
914 if ( ellipsoid != geoNone() )
915 {
916 str = formatDistance( dist );
917 derivedAttributes.insert( tr( "Length (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
918 }
919
920 str = formatDistance( layerCrsGeom->length() * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
921 if ( QgsWkbTypes::hasZ( layerCrsGeom->wkbType() )
923 {
924 // 3d linestring (or multiline)
925 derivedAttributes.insert( tr( "Length (Cartesian — 2D)" ), str );
926
927 double totalLength3d = std::accumulate( layerCrsGeom->const_parts_begin(), layerCrsGeom->const_parts_end(), 0.0, []( double total, const QgsAbstractGeometry *part ) {
928 return total + qgsgeometry_cast<const QgsLineString *>( part )->length3D();
929 } );
930
931 str = formatDistance( totalLength3d, cartesianDistanceUnits );
932 derivedAttributes.insert( tr( "Length (Cartesian — 3D)" ), str );
933 }
934 else
935 {
936 derivedAttributes.insert( tr( "Length (Cartesian)" ), str );
937 }
938
939 str = QLocale().toString( layerCrsGeom->nCoordinates() );
940 derivedAttributes.insert( tr( "Vertices" ), str );
941 if ( !layerPoint.isEmpty() )
942 {
943 //add details of closest vertex to identify point
944 closestVertexAttributes( layerToMapTransform, layerVertCrs, mapVertCrs, *layerCrsGeom, vId, showTransformedZ, derivedAttributes );
945 closestPointAttributes( layerToMapTransform, layerVertCrs, mapVertCrs, *layerCrsGeom, layerPoint, showTransformedZ, derivedAttributes );
946 }
947
948 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( layerCrsGeom ) )
949 {
950 // Add the start and end points in as derived attributes
951 QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->startPoint().x(), curve->startPoint().y() ) );
952 QString x;
953 QString y;
954 formatCoordinate( pnt, x, y );
955 derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), x );
956 derivedAttributes.insert( tr( "firstY" ), y );
957 pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->endPoint().x(), curve->endPoint().y() ) );
958 formatCoordinate( pnt, x, y );
959 derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), x );
960 derivedAttributes.insert( tr( "lastY" ), y );
961 }
962 }
963 else if ( geometryType == Qgis::GeometryType::Polygon )
964 {
965 double area = 0;
966 try
967 {
968 area = calc.measureArea( layerCrsGeometry );
969 area = calc.convertAreaMeasurement( area, displayAreaUnits() );
970 }
971 catch ( QgsCsException & )
972 {
973 // TODO report errors to user
974 QgsDebugError( QStringLiteral( "An error occurred while calculating area" ) );
975 }
976
977 QString str;
978 if ( ellipsoid != geoNone() )
979 {
980 str = formatArea( area );
981 derivedAttributes.insert( tr( "Area (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
982 }
983 str = formatArea( layerCrsGeometry.area() * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() ), cartesianAreaUnits ), cartesianAreaUnits );
984 derivedAttributes.insert( tr( "Area (Cartesian)" ), str );
985
986 if ( ellipsoid != geoNone() )
987 {
988 double perimeter = 0;
989 try
990 {
991 perimeter = calc.measurePerimeter( layerCrsGeometry );
992 perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
993 }
994 catch ( QgsCsException & )
995 {
996 // TODO report errors to user
997 QgsDebugError( QStringLiteral( "An error occurred while calculating perimeter" ) );
998 }
999 str = formatDistance( perimeter );
1000 derivedAttributes.insert( tr( "Perimeter (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
1001 }
1002 str = formatDistance( layerCrsGeometry.constGet()->perimeter() * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
1003 derivedAttributes.insert( tr( "Perimeter (Cartesian)" ), str );
1004
1005 str = QLocale().toString( layerCrsGeometry.constGet()->nCoordinates() );
1006 derivedAttributes.insert( tr( "Vertices" ), str );
1007
1008 if ( !layerPoint.isEmpty() )
1009 {
1010 //add details of closest vertex to identify point
1011 closestVertexAttributes( layerToMapTransform, layerVertCrs, mapVertCrs, *layerCrsGeometry.constGet(), vId, showTransformedZ, derivedAttributes );
1012 closestPointAttributes( layerToMapTransform, layerVertCrs, mapVertCrs, *layerCrsGeometry.constGet(), layerPoint, showTransformedZ, derivedAttributes );
1013 }
1014 }
1015 else if ( geometryType == Qgis::GeometryType::Point )
1016 {
1017 // Include the x, y, z coordinates of the point as a derived attribute
1018 if ( const QgsPoint *mapCrsPoint = qgsgeometry_cast<const QgsPoint *>( mapCrsGeometry.constGet() ) )
1019 {
1020 QString x;
1021 QString y;
1022 formatCoordinate( QgsPointXY( mapCrsPoint->x(), mapCrsPoint->y() ), x, y );
1023 derivedAttributes.insert( tr( "X" ), x );
1024 derivedAttributes.insert( tr( "Y" ), y );
1025
1026 const double originalZ = QgsWkbTypes::hasZ( wkbType ) ? qgsgeometry_cast<const QgsPoint *>( layerCrsGeometry.constGet() )->z()
1027 : std::numeric_limits<double>::quiet_NaN();
1028 const double mapCrsZ = mapCrsPoint->is3D() ? mapCrsPoint->z() : std::numeric_limits<double>::quiet_NaN();
1029
1030 if ( !std::isnan( originalZ ) )
1031 {
1032 const QString str = QLocale().toString( originalZ, 'g', 10 );
1033 derivedAttributes.insert( showTransformedZ ? tr( "Z (%1)" ).arg( layerVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ) : tr( "Z" ), str );
1034 }
1035 if ( showTransformedZ && !std::isnan( mapCrsZ ) && !qgsDoubleNear( originalZ, mapCrsZ ) )
1036 {
1037 const QString str = QLocale().toString( mapCrsZ, 'g', 10 );
1038 derivedAttributes.insert( tr( "Z (%1)" ).arg( mapVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ), str );
1039 }
1040
1041 if ( QgsWkbTypes::hasM( wkbType ) )
1042 {
1043 const QString str = QLocale().toString( qgsgeometry_cast<const QgsPoint *>( layerCrsGeometry.constGet() )->m(), 'g', 10 );
1044 derivedAttributes.insert( tr( "M" ), str );
1045 }
1046 }
1047 else
1048 {
1049 //multipoint
1050 if ( !layerPoint.isEmpty() )
1051 {
1052 //add details of closest vertex to identify point
1053 const QgsAbstractGeometry *geom = layerCrsGeometry.constGet();
1054 closestVertexAttributes( layerToMapTransform, layerVertCrs, mapVertCrs, *geom, vId, showTransformedZ, derivedAttributes );
1055 }
1056 }
1057 }
1058
1059 if ( feature.embeddedSymbol() )
1060 {
1061 derivedAttributes.insert( tr( "Embedded Symbol" ), tr( "%1 (%2)" ).arg( QgsSymbol::symbolTypeToString( feature.embeddedSymbol()->type() ), feature.embeddedSymbol()->color().name() ) );
1062 }
1063
1064 return derivedAttributes;
1065}
1066
1067bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
1068{
1069 QgsPointXY point = geometry.asPoint(); // raster layers currently only support identification by point
1070 return identifyRasterLayer( results, layer, point, viewExtent, mapUnitsPerPixel, identifyContext );
1071}
1072
1073bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
1074{
1075 QgsDebugMsgLevel( "point = " + point.toString(), 2 );
1076 if ( !layer )
1077 return false;
1078
1079 std::unique_ptr<QgsRasterDataProvider> dprovider( layer->dataProvider()->clone() );
1080 if ( !dprovider )
1081 return false;
1082
1083 const Qgis::RasterInterfaceCapabilities capabilities = dprovider->capabilities();
1084 if ( !( capabilities & Qgis::RasterInterfaceCapability::Identify ) )
1085 return false;
1086
1087 if ( identifyContext.isTemporal() )
1088 {
1089 if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
1090 return false;
1091
1092 dprovider->temporalCapabilities()->setRequestedTemporalRange( identifyContext.temporalRange() );
1093 }
1094
1095 if ( !identifyContext.zRange().isInfinite() )
1096 {
1097 if ( !layer->elevationProperties()->isVisibleInZRange( identifyContext.zRange(), layer ) )
1098 return false;
1099 }
1100
1101 QgsPointXY pointInCanvasCrs = point;
1102 try
1103 {
1104 point = toLayerCoordinates( layer, point );
1105 }
1106 catch ( QgsCsException &cse )
1107 {
1108 Q_UNUSED( cse )
1109 QgsDebugError( QStringLiteral( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
1110 return false;
1111 }
1112 QgsDebugMsgLevel( QStringLiteral( "point = %1 %2" ).arg( point.x() ).arg( point.y() ), 2 );
1113
1114 if ( !layer->extent().contains( point ) )
1115 return false;
1116
1117 QMap<QString, QString> attributes, derivedAttributes;
1118
1119 Qgis::RasterIdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
1120
1121 // check if the format is really supported otherwise use first supported format
1122 if ( !( capabilities & QgsRasterDataProvider::identifyFormatToCapability( format ) ) )
1123 {
1126 else if ( capabilities & Qgis::RasterInterfaceCapability::IdentifyValue )
1128 else if ( capabilities & Qgis::RasterInterfaceCapability::IdentifyHtml )
1130 else if ( capabilities & Qgis::RasterInterfaceCapability::IdentifyText )
1132 else
1133 return false;
1134 }
1135
1136 QgsRasterIdentifyResult identifyResult;
1137 // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
1138 if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
1139 {
1140 // To get some reasonable response for point/line WMS vector layers we must
1141 // use a context with approximately a resolution in layer CRS units
1142 // corresponding to current map canvas resolution (for examplei UMN Mapserver
1143 // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
1144 // + TOLERANCE (layer param) for feature selection)
1145 //
1146 QgsRectangle r;
1147 r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
1148 r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
1149 r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
1150 r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
1151 r = toLayerCoordinates( layer, r ); // will be a bit larger
1152 // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
1153 // but that is fixed (the rect is enlarged) in the WMS provider
1154 identifyResult = dprovider->identify( point, format, r, 1, 1 );
1155 }
1156 else
1157 {
1158 // It would be nice to use the same extent and size which was used for drawing,
1159 // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
1160 // is doing some tricks with extent and size to align raster to output which
1161 // would be difficult to replicate here.
1162 // Note: cutting the extent may result in slightly different x and y resolutions
1163 // and thus shifted point calculated back in QGIS WMS (using average resolution)
1164 //viewExtent = dprovider->extent().intersect( &viewExtent );
1165
1166 // Width and height are calculated from not projected extent and we hope that
1167 // are similar to source width and height used to reproject layer for drawing.
1168 // TODO: may be very dangerous, because it may result in different resolutions
1169 // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
1170 int width = static_cast<int>( std::round( viewExtent.width() / mapUnitsPerPixel ) );
1171 int height = static_cast<int>( std::round( viewExtent.height() / mapUnitsPerPixel ) );
1172
1173 QgsDebugMsgLevel( QStringLiteral( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ), 2 );
1174 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( width ).arg( height ), 2 );
1175 QgsDebugMsgLevel( QStringLiteral( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ), 2 );
1176
1177 identifyResult = dprovider->identify( point, format, viewExtent, width, height );
1178 }
1179
1180 QgsRasterLayerElevationProperties *elevationProperties = qobject_cast<QgsRasterLayerElevationProperties *>( layer->elevationProperties() );
1181 if ( identifyResult.isValid() && !identifyContext.zRange().isInfinite() && elevationProperties && elevationProperties->isEnabled() )
1182 {
1183 // filter results by z range
1184 switch ( format )
1185 {
1187 {
1188 bool foundMatch = false;
1189 QMap<int, QVariant> values = identifyResult.results();
1190 QMap<int, QVariant> filteredValues;
1191 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1192 {
1193 if ( QgsVariantUtils::isNull( it.value() ) )
1194 {
1195 continue;
1196 }
1197 const double value = it.value().toDouble();
1198 const QgsDoubleRange elevationRange = elevationProperties->elevationRangeForPixelValue( layer, it.key(), value );
1199 if ( !elevationRange.isInfinite() && identifyContext.zRange().overlaps( elevationRange ) )
1200 {
1201 filteredValues.insert( it.key(), it.value() );
1202 foundMatch = true;
1203 }
1204 }
1205
1206 if ( !foundMatch )
1207 return false;
1208
1209 identifyResult = QgsRasterIdentifyResult( Qgis::RasterIdentifyFormat::Value, filteredValues );
1210
1211 break;
1212 }
1213
1214 // can't filter by z for these formats
1219 break;
1220 }
1221 }
1222
1223 derivedAttributes.insert( derivedAttributesForPoint( QgsPoint( pointInCanvasCrs ) ) );
1224
1225 const double xres = layer->rasterUnitsPerPixelX();
1226 const double yres = layer->rasterUnitsPerPixelY();
1227 QgsRectangle pixelRect;
1228 // Don't derive clicked column/row for providers that serve dynamically rendered map images
1229 if ( ( dprovider->capabilities() & Qgis::RasterInterfaceCapability::Size ) && !qgsDoubleNear( xres, 0 ) && !qgsDoubleNear( yres, 0 ) )
1230 {
1231 // Try to determine the clicked column/row (0-based) in the raster
1232 const QgsRectangle extent = dprovider->extent();
1233
1234 const int rasterCol = static_cast<int>( std::floor( ( point.x() - extent.xMinimum() ) / xres ) );
1235 const int rasterRow = static_cast<int>( std::floor( ( extent.yMaximum() - point.y() ) / yres ) );
1236
1237 derivedAttributes.insert( tr( "Column (0-based)" ), QLocale().toString( rasterCol ) );
1238 derivedAttributes.insert( tr( "Row (0-based)" ), QLocale().toString( rasterRow ) );
1239
1240 pixelRect = QgsRectangle( rasterCol * xres + extent.xMinimum(), extent.yMaximum() - ( rasterRow + 1 ) * yres, ( rasterCol + 1 ) * xres + extent.xMinimum(), extent.yMaximum() - ( rasterRow * yres ) );
1241 }
1242
1243 if ( identifyResult.isValid() )
1244 {
1245 QMap<int, QVariant> values = identifyResult.results();
1246 if ( format == Qgis::RasterIdentifyFormat::Value )
1247 {
1248 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1249 {
1250 QString valueString;
1251 if ( QgsVariantUtils::isNull( it.value() ) )
1252 {
1253 valueString = tr( "no data" );
1254 }
1255 else
1256 {
1257 QVariant value( it.value() );
1258 // The cast is legit. Quoting QT doc :
1259 // "Although this function is declared as returning QVariant::Type,
1260 // the return value should be interpreted as QMetaType::Type"
1261 if ( static_cast<QMetaType::Type>( value.userType() ) == QMetaType::Float )
1262 {
1263 valueString = QgsRasterBlock::printValue( value.toFloat(), true );
1264 }
1265 else
1266 {
1267 valueString = QgsRasterBlock::printValue( value.toDouble(), true );
1268 }
1269 }
1270 attributes.insert( dprovider->generateBandName( it.key() ), valueString );
1271
1272 // Get raster attribute table attributes
1273 if ( const QgsRasterAttributeTable *rat = layer->attributeTable( it.key() ) )
1274 {
1275 bool ok;
1276 const double doubleValue { it.value().toDouble( &ok ) };
1277 if ( ok )
1278 {
1279 const QVariantList row = rat->row( doubleValue );
1280 if ( !row.isEmpty() )
1281 {
1282 for ( int colIdx = 0; colIdx < std::min( rat->fields().count(), row.count() ); ++colIdx )
1283 {
1284 const QgsRasterAttributeTable::Field ratField { rat->fields().at( colIdx ) };
1285
1286 // Skip value and color fields
1287 if ( QgsRasterAttributeTable::valueAndColorFieldUsages().contains( ratField.usage ) )
1288 {
1289 continue;
1290 }
1291
1292 QString ratValue;
1293 switch ( ratField.type )
1294 {
1295 case QMetaType::Type::QChar:
1296 case QMetaType::Type::Int:
1297 case QMetaType::Type::UInt:
1298 case QMetaType::Type::LongLong:
1299 case QMetaType::Type::ULongLong:
1300 ratValue = QLocale().toString( row.at( colIdx ).toLongLong() );
1301 break;
1302 case QMetaType::Type::Double:
1303 ratValue = QLocale().toString( row.at( colIdx ).toDouble() );
1304 break;
1305 default:
1306 ratValue = row.at( colIdx ).toString();
1307 }
1308 attributes.insert( ratField.name, ratValue );
1309 }
1310 }
1311 }
1312 } // end RAT
1313 }
1314
1315 QString label = layer->name();
1316 QgsFeature feature;
1317 if ( !pixelRect.isNull() )
1318 {
1319 feature.setGeometry( QgsGeometry::fromRect( pixelRect ) );
1320 }
1321
1322 IdentifyResult result( qobject_cast<QgsMapLayer *>( layer ), label, QgsFields(), feature, derivedAttributes );
1323 result.mAttributes = attributes;
1324 results->append( result );
1325 }
1326 else if ( format == Qgis::RasterIdentifyFormat::Feature )
1327 {
1328 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1329 {
1330 QVariant value = it.value();
1331 if ( value.userType() == QMetaType::Type::Bool && !value.toBool() )
1332 {
1333 // sublayer not visible or not queryable
1334 continue;
1335 }
1336
1337 if ( value.userType() == QMetaType::Type::QString )
1338 {
1339 // error
1340 // TODO: better error reporting
1341 QString label = layer->subLayers().value( it.key() );
1342 attributes.clear();
1343 attributes.insert( tr( "Error" ), value.toString() );
1344
1345 results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1346 continue;
1347 }
1348
1349 // list of feature stores for a single sublayer
1350 const QgsFeatureStoreList featureStoreList = value.value<QgsFeatureStoreList>();
1351
1352 for ( const QgsFeatureStore &featureStore : featureStoreList )
1353 {
1354 const QgsFeatureList storeFeatures = featureStore.features();
1355 for ( const QgsFeature &feature : storeFeatures )
1356 {
1357 attributes.clear();
1358 // WMS sublayer and feature type, a sublayer may contain multiple feature types.
1359 // Sublayer name may be the same as layer name and feature type name
1360 // may be the same as sublayer. We try to avoid duplicities in label.
1361 QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
1362 QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
1363 // Strip UMN MapServer '_feature'
1364 featureType.remove( QStringLiteral( "_feature" ) );
1365 QStringList labels;
1366 if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
1367 {
1368 labels << sublayer;
1369 }
1370 if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
1371 {
1372 labels << featureType;
1373 }
1374
1375 QMap<QString, QString> derAttributes = derivedAttributes;
1376 derAttributes.insert( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
1377
1378 IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QLatin1String( " / " ) ), featureStore.fields(), feature, derAttributes );
1379
1380 identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
1381 results->append( identifyResult );
1382 }
1383 }
1384 }
1385 }
1386 else // text or html
1387 {
1388 QgsDebugMsgLevel( QStringLiteral( "%1 HTML or text values" ).arg( values.size() ), 2 );
1389 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1390 {
1391 QString value = it.value().toString();
1392 attributes.clear();
1393 attributes.insert( QString(), value );
1394
1395 QString label = layer->subLayers().value( it.key() );
1396 results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1397 }
1398 }
1399 }
1400 else
1401 {
1402 attributes.clear();
1403 QString value = identifyResult.error().message( QgsErrorMessage::Text );
1404 attributes.insert( tr( "Error" ), value );
1405 QString label = tr( "Identify error" );
1406 results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1407 }
1408
1409 return true;
1410}
1411
1412Qgis::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
1413{
1414 return mCanvas->mapUnits();
1415}
1416
1417Qgis::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
1418{
1419 return QgsUnitTypes::distanceToAreaUnit( mCanvas->mapUnits() );
1420}
1421
1422QString QgsMapToolIdentify::formatDistance( double distance ) const
1423{
1424 return formatDistance( distance, displayDistanceUnits() );
1425}
1426
1427QString QgsMapToolIdentify::formatArea( double area ) const
1428{
1429 return formatArea( area, displayAreaUnits() );
1430}
1431
1432QString QgsMapToolIdentify::formatDistance( double distance, Qgis::DistanceUnit unit ) const
1433{
1434 QgsSettings settings;
1435 bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1436
1437 return QgsDistanceArea::formatDistance( distance, mCoordinatePrecision, unit, baseUnit );
1438}
1439
1440QString QgsMapToolIdentify::formatArea( double area, Qgis::AreaUnit unit ) const
1441{
1442 QgsSettings settings;
1443 bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1444
1445 return QgsDistanceArea::formatArea( area, mCoordinatePrecision, unit, baseUnit );
1446}
1447
1449{
1450 QList<IdentifyResult> results;
1451 if ( identifyRasterLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel ) )
1452 {
1453 emit changedRasterResults( results );
1454 }
1455}
1456
1457void QgsMapToolIdentify::fromPointCloudIdentificationToIdentifyResults( QgsPointCloudLayer *layer, const QVector<QVariantMap> &identified, QList<QgsMapToolIdentify::IdentifyResult> &results )
1458{
1462 : layer->crs3D();
1463 const bool showTransformedZ = QgsProject::instance()->crs3D() != layer->crs3D() && QgsProject::instance()->crs3D().hasVerticalAxis() && layer->crs3D().hasVerticalAxis();
1465
1466 int id = 1;
1467 const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast<const QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
1468 for ( const QVariantMap &pt : identified )
1469 {
1470 QMap<QString, QString> ptStr;
1471 QString classification;
1472 for ( auto attrIt = pt.constBegin(); attrIt != pt.constEnd(); ++attrIt )
1473 {
1474 if ( attrIt.key().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) == 0
1475 && ( !qgsDoubleNear( elevationProps->zScale(), 1 ) || !qgsDoubleNear( elevationProps->zOffset(), 0 ) ) )
1476 {
1477 // Apply elevation properties
1478 ptStr[tr( "Z (original)" )] = attrIt.value().toString();
1479 ptStr[tr( "Z (adjusted)" )] = QString::number( attrIt.value().toDouble() * elevationProps->zScale() + elevationProps->zOffset() );
1480 }
1481 else if ( attrIt.key().compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
1482 {
1483 classification = QgsPointCloudDataProvider::translatedLasClassificationCodes().value( attrIt.value().toInt() );
1484 ptStr[attrIt.key()] = QStringLiteral( "%1 (%2)" ).arg( attrIt.value().toString(), classification );
1485 }
1486 else
1487 {
1488 ptStr[attrIt.key()] = attrIt.value().toString();
1489 }
1490 }
1491
1492 QMap<QString, QString> derivedAttributes;
1493 QgsPoint layerPoint( pt.value( "X" ).toDouble(), pt.value( "Y" ).toDouble(), pt.value( "Z" ).toDouble() );
1494
1495 QgsPoint mapCrsPoint = layerPoint;
1496 try
1497 {
1498 if ( layerToMapTransform.isValid() )
1499 {
1500 mapCrsPoint.transform( layerToMapTransform, Qgis::TransformDirection::Forward, layerToMapTransform.hasVerticalComponent() );
1501 }
1502 }
1503 catch ( QgsCsException &cse )
1504 {
1505 QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
1506 }
1507
1508 QString x;
1509 QString y;
1510 // BAD, we should not be using the hardcoded precision/crs values here, but this method is static and that's not trivial
1511 // to avoid...
1512 formatCoordinate( QgsPointXY( mapCrsPoint.x(), mapCrsPoint.y() ), x, y, QgsProject::instance()->crs(), 6 );
1513 derivedAttributes.insert( tr( "X" ), x );
1514 derivedAttributes.insert( tr( "Y" ), y );
1515
1516 const double originalZ = layerPoint.z();
1517 const double mapCrsZ = mapCrsPoint.is3D() ? mapCrsPoint.z() : std::numeric_limits<double>::quiet_NaN();
1518
1519 if ( !std::isnan( originalZ ) )
1520 {
1521 const QString str = QLocale().toString( originalZ, 'g', 10 );
1522 derivedAttributes.insert( showTransformedZ ? tr( "Z (%1)" ).arg( layerVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ) : tr( "Z" ), str );
1523 }
1524 if ( showTransformedZ && !std::isnan( mapCrsZ ) && !qgsDoubleNear( originalZ, mapCrsZ ) )
1525 {
1526 const QString str = QLocale().toString( mapCrsZ, 'g', 10 );
1527 derivedAttributes.insert( tr( "Z (%1)" ).arg( mapVertCrs.userFriendlyIdentifier( Qgis::CrsIdentifierType::MediumString ) ), str );
1528 }
1529
1530 QgsMapToolIdentify::IdentifyResult res( layer, classification.isEmpty() ? QString::number( id ) : QStringLiteral( "%1 (%2)" ).arg( id ).arg( classification ), ptStr, derivedAttributes );
1531 results.append( res );
1532 ++id;
1533 }
1534}
1535
1536void QgsMapToolIdentify::fromElevationProfileLayerIdentificationToIdentifyResults( QgsMapLayer *layer, const QVector<QVariantMap> &identified, QList<IdentifyResult> &results )
1537{
1538 if ( !layer )
1539 return;
1540
1541 if ( identified.empty() )
1542 return;
1543
1544 switch ( layer->type() )
1545 {
1547 {
1548 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
1549
1550 QgsFeatureList features;
1551 QHash<QgsFeatureId, QVariant> featureDistances;
1552 QHash<QgsFeatureId, QVariant> featureElevations;
1553
1554 QgsFeatureIds filterIds;
1555 for ( const QVariantMap &map : identified )
1556 {
1557 if ( !map.contains( QStringLiteral( "id" ) ) )
1558 {
1559 QMap<QString, QString> attributes;
1560 if ( map.value( QStringLiteral( "distance" ) ).isValid() )
1561 attributes.insert( tr( "Distance along curve" ), QString::number( map.value( QStringLiteral( "distance" ) ).toDouble() ) );
1562 if ( map.value( QStringLiteral( "elevation" ) ).isValid() )
1563 attributes.insert( tr( "Elevation" ), QString::number( map.value( QStringLiteral( "elevation" ) ).toDouble() ) );
1564
1565 results.append( IdentifyResult( layer, layer->name(), {}, attributes ) );
1566 }
1567 else
1568 {
1569 const QgsFeatureId id = map.value( QStringLiteral( "id" ) ).toLongLong();
1570 filterIds.insert( id );
1571
1572 featureDistances.insert( id, map.value( QStringLiteral( "distance" ) ) );
1573 featureElevations.insert( id, map.value( QStringLiteral( "elevation" ) ) );
1574 }
1575 }
1576
1577 QgsFeatureRequest request;
1578 request.setFilterFids( filterIds );
1579 QgsFeatureIterator it = vl->getFeatures( request );
1580 QgsFeature f;
1581 while ( it.nextFeature( f ) )
1582 features << f;
1583
1584 QgsRenderContext context;
1585 identifyVectorLayer( &results, vl, features, nullptr, QMap<QString, QString>(), [this, vl, &featureDistances, &featureElevations]( const QgsFeature &feature ) -> QMap<QString, QString> {
1586 QMap< QString, QString > attributes = featureDerivedAttributes( feature, vl, QgsPointXY() );
1587
1588 if ( featureDistances.value( feature.id() ).isValid() )
1589 attributes.insert( tr( "Distance along curve" ), QString::number( featureDistances.value( feature.id() ).toDouble() ) );
1590 if ( featureElevations.value( feature.id() ).isValid() )
1591 attributes.insert( tr( "Elevation" ), QString::number( featureElevations.value( feature.id() ).toDouble() ) );
1592
1593 return attributes; }, context );
1594 break;
1595 }
1596
1599 {
1600 for ( const QVariantMap &map : identified )
1601 {
1602 QMap<QString, QString> attributes;
1603 if ( map.value( QStringLiteral( "distance" ) ).isValid() )
1604 attributes.insert( tr( "Distance along curve" ), QString::number( map.value( QStringLiteral( "distance" ) ).toDouble() ) );
1605 if ( map.value( QStringLiteral( "elevation" ) ).isValid() )
1606 attributes.insert( tr( "Elevation" ), QString::number( map.value( QStringLiteral( "elevation" ) ).toDouble() ) );
1607
1608 results.append( IdentifyResult( layer, layer->name(), {}, attributes ) );
1609 }
1610
1611 break;
1612 }
1613
1615 {
1616 QgsPointCloudLayer *pcLayer = qobject_cast<QgsPointCloudLayer *>( layer );
1617 fromPointCloudIdentificationToIdentifyResults( pcLayer, identified, results );
1618 break;
1619 }
1620
1626 break;
1627 }
1628}
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:4722
@ 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:4582
AreaUnit
Units of area.
Definition qgis.h:4799
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:4541
@ 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.
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.
QPointer< QgsMapCanvas > mCanvas
The pointer to the map canvas.
Definition qgsmaptool.h:338
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
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
double yMaximum
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:294
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.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition qgis.h:6519
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6024
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
QMap< QString, QVariant > mParams
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:45
int part
Part number.
Definition qgsvertexid.h:88
Qgis::VertexType type
Vertex type.
Definition qgsvertexid.h:97