QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgsmaptopixel.h"
29 #include "qgsmessageviewer.h"
30 #include "qgsmeshlayer.h"
32 #include "qgsmaplayer.h"
33 #include "qgsrasterdataprovider.h"
34 #include "qgsrasterlayer.h"
37 #include "qgsvectordataprovider.h"
38 #include "qgsvectorlayer.h"
40 #include "qgsvectortilelayer.h"
42 #include "qgsvectortileutils.h"
43 #include "qgsproject.h"
44 #include "qgsrenderer.h"
45 #include "qgstiles.h"
46 #include "qgsgeometryutils.h"
47 #include "qgsgeometrycollection.h"
48 #include "qgscurve.h"
49 #include "qgscoordinateutils.h"
50 #include "qgsexception.h"
51 #include "qgssettings.h"
53 #include "qgspointcloudlayer.h"
54 #include "qgspointcloudrenderer.h"
57 #include "qgssymbol.h"
58 
59 #include <QMouseEvent>
60 #include <QCursor>
61 #include <QPixmap>
62 #include <QStatusBar>
63 #include <QVariant>
64 
66  : QgsMapTool( canvas )
67  , mIdentifyMenu( new QgsIdentifyMenu( mCanvas ) )
68  , mLastMapUnitsPerPixel( -1.0 )
69  , mCoordinatePrecision( 6 )
70 {
71  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Identify ) );
72 }
73 
75 {
76  delete mIdentifyMenu;
77 }
78 
80 {
81  Q_UNUSED( e )
82 }
83 
85 {
86  Q_UNUSED( e )
87 }
88 
90 {
91  Q_UNUSED( e )
92 }
93 
94 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, const QList<QgsMapLayer *> &layerList, IdentifyMode mode, const QgsIdentifyContext &identifyContext )
95 {
96  return identify( x, y, mode, layerList, AllLayers, identifyContext );
97 }
98 
99 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, LayerType layerType, const QgsIdentifyContext &identifyContext )
100 {
101  return identify( x, y, mode, QList<QgsMapLayer *>(), layerType, identifyContext );
102 }
103 
104 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType, const QgsIdentifyContext &identifyContext )
105 {
106  return identify( QgsGeometry::fromPointXY( toMapCoordinates( QPoint( x, y ) ) ), mode, layerList, layerType, identifyContext );
107 }
108 
109 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, LayerType layerType, const QgsIdentifyContext &identifyContext )
110 {
111  return identify( geometry, mode, QList<QgsMapLayer *>(), layerType, identifyContext );
112 }
113 
114 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType, const QgsIdentifyContext &identifyContext )
115 {
116  QList<IdentifyResult> results;
117 
118  mLastGeometry = geometry;
119  mLastExtent = mCanvas->extent();
120  mLastMapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
121 
122  mCoordinatePrecision = QgsCoordinateUtils::calculateCoordinatePrecision( mLastMapUnitsPerPixel, mCanvas->mapSettings().destinationCrs() );
123 
124  if ( mode == DefaultQgsSetting )
125  {
126  QgsSettings settings;
127  mode = settings.enumValue( QStringLiteral( "Map/identifyMode" ), ActiveLayer );
128  }
129 
130  if ( mode == LayerSelection )
131  {
132  QPoint canvasPt = toCanvasCoordinates( geometry.asPoint() );
133  int x = canvasPt.x(), y = canvasPt.y();
134  QList<IdentifyResult> results = identify( x, y, TopDownAll, layerList, layerType, identifyContext );
135  QPoint globalPos = mCanvas->mapToGlobal( QPoint( x + 5, y + 5 ) );
136  return mIdentifyMenu->exec( results, globalPos );
137  }
138  else if ( mode == ActiveLayer && layerList.isEmpty() )
139  {
140  QgsMapLayer *layer = mCanvas->currentLayer();
141 
142  if ( !layer )
143  {
144  emit identifyMessage( tr( "No active layer. To identify features, you must choose an active layer." ) );
145  return results;
146  }
147  if ( !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
148  return results;
149 
150  QApplication::setOverrideCursor( Qt::WaitCursor );
151 
152  identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType, identifyContext );
153  }
154  else
155  {
156  QApplication::setOverrideCursor( Qt::WaitCursor );
157 
158  int layerCount;
159  if ( layerList.isEmpty() )
160  layerCount = mCanvas->layerCount();
161  else
162  layerCount = layerList.count();
163 
164 
165  for ( int i = 0; i < layerCount; i++ )
166  {
167 
168  QgsMapLayer *layer = nullptr;
169  if ( layerList.isEmpty() )
170  layer = mCanvas->layer( i );
171  else
172  layer = layerList.value( i );
173 
174  emit identifyProgress( i, mCanvas->layerCount() );
175  emit identifyMessage( tr( "Identifying on %1…" ).arg( layer->name() ) );
176 
177  if ( !layer->flags().testFlag( QgsMapLayer::Identifiable ) )
178  continue;
179 
180  if ( identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType, identifyContext ) )
181  {
182  if ( mode == TopDownStopAtFirst )
183  break;
184  }
185  }
186 
188  emit identifyMessage( tr( "Identifying done." ) );
189  }
190 
191  QApplication::restoreOverrideCursor();
192 
193  return results;
194 }
195 
196 void QgsMapToolIdentify::setCanvasPropertiesOverrides( double searchRadiusMapUnits )
197 {
198  mOverrideCanvasSearchRadius = searchRadiusMapUnits;
199 }
200 
202 {
203  mOverrideCanvasSearchRadius = -1;
204 }
205 
207 {
209 }
210 
212 {
214 }
215 
216 bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType, const QgsIdentifyContext &identifyContext )
217 {
218  return identifyLayer( results, layer, QgsGeometry::fromPointXY( point ), viewExtent, mapUnitsPerPixel, layerType, identifyContext );
219 }
220 
221 bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType, const QgsIdentifyContext &identifyContext )
222 {
223  if ( layer->type() == QgsMapLayerType::RasterLayer && layerType.testFlag( RasterLayer ) )
224  {
225  return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), geometry, viewExtent, mapUnitsPerPixel, identifyContext );
226  }
227  else if ( layer->type() == QgsMapLayerType::VectorLayer && layerType.testFlag( VectorLayer ) )
228  {
229  return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), geometry, identifyContext );
230  }
231  else if ( layer->type() == QgsMapLayerType::MeshLayer && layerType.testFlag( MeshLayer ) )
232  {
233  return identifyMeshLayer( results, qobject_cast<QgsMeshLayer *>( layer ), geometry, identifyContext );
234  }
235  else if ( layer->type() == QgsMapLayerType::VectorTileLayer && layerType.testFlag( VectorTileLayer ) )
236  {
237  return identifyVectorTileLayer( results, qobject_cast<QgsVectorTileLayer *>( layer ), geometry, identifyContext );
238  }
239  else if ( layer->type() == QgsMapLayerType::PointCloudLayer && layerType.testFlag( PointCloudLayer ) )
240  {
241  return identifyPointCloudLayer( results, qobject_cast<QgsPointCloudLayer *>( layer ), geometry, identifyContext );
242  }
243  else
244  {
245  return false;
246  }
247 }
248 
249 bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext )
250 {
251  return identifyVectorLayer( results, layer, QgsGeometry::fromPointXY( point ), identifyContext );
252 }
253 
254 bool QgsMapToolIdentify::identifyMeshLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMeshLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
255 {
256  const QgsPointXY point = geometry.asPoint(); // mesh layers currently only support identification by point
257  return identifyMeshLayer( results, layer, point, identifyContext );
258 }
259 
260 bool QgsMapToolIdentify::identifyMeshLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMeshLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext )
261 {
262  QgsDebugMsgLevel( "point = " + point.toString(), 4 );
263  if ( !layer )
264  return false;
265 
266  double searchRadius = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
267  bool isTemporal = identifyContext.isTemporal() && layer->temporalProperties()->isActive();
268 
269  QList<QgsMeshDatasetIndex> datasetIndexList;
270  int activeScalarGroup = layer->rendererSettings().activeScalarDatasetGroup();
271  int activeVectorGroup = layer->rendererSettings().activeVectorDatasetGroup();
272 
273  const QList<int> allGroup = layer->enabledDatasetGroupsIndexes();
274  if ( isTemporal ) //non active dataset group value are only accesible if temporal is active
275  {
276  const QgsDateTimeRange &time = identifyContext.temporalRange();
277  if ( activeScalarGroup >= 0 )
278  datasetIndexList.append( layer->activeScalarDatasetAtTime( time ) );
279  if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
280  datasetIndexList.append( layer->activeVectorDatasetAtTime( time ) );
281 
282  for ( int groupIndex : allGroup )
283  {
284  if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
285  datasetIndexList.append( layer->datasetIndexAtTime( time, groupIndex ) );
286  }
287  }
288  else
289  {
290  // only active dataset group
291  if ( activeScalarGroup >= 0 )
292  datasetIndexList.append( layer->staticScalarDatasetIndex() );
293  if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
294  datasetIndexList.append( layer->staticVectorDatasetIndex() );
295 
296  // ...and static dataset group
297  for ( int groupIndex : allGroup )
298  {
299  if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
300  {
301  if ( !layer->datasetGroupMetadata( groupIndex ).isTemporal() )
302  datasetIndexList.append( groupIndex );
303  }
304  }
305  }
306 
307  //create results
308  for ( const QgsMeshDatasetIndex &index : datasetIndexList )
309  {
310  if ( !index.isValid() )
311  continue;
312 
313  const QgsMeshDatasetGroupMetadata &groupMeta = layer->datasetGroupMetadata( index );
314  QMap< QString, QString > derivedAttributes;
315 
316  QMap<QString, QString> attribute;
317  if ( groupMeta.isScalar() )
318  {
319  const QgsMeshDatasetValue scalarValue = layer->datasetValue( index, point, searchRadius );
320  const double scalar = scalarValue.scalar();
321  attribute.insert( tr( "Scalar Value" ), std::isnan( scalar ) ? tr( "no data" ) : QString::number( scalar ) );
322  }
323 
324  if ( groupMeta.isVector() )
325  {
326  const QgsMeshDatasetValue vectorValue = layer->datasetValue( index, point, searchRadius );
327  const double vectorX = vectorValue.x();
328  const double vectorY = vectorValue.y();
329  if ( std::isnan( vectorX ) || std::isnan( vectorY ) )
330  attribute.insert( tr( "Vector Value" ), tr( "no data" ) );
331  else
332  {
333  attribute.insert( tr( "Vector Magnitude" ), QString::number( vectorValue.scalar() ) );
334  derivedAttributes.insert( tr( "Vector x-component" ), QString::number( vectorY ) );
335  derivedAttributes.insert( tr( "Vector y-component" ), QString::number( vectorX ) );
336  }
337  }
338 
339  const QgsMeshDatasetMetadata &meta = layer->datasetMetadata( index );
340 
341  if ( groupMeta.isTemporal() )
342  derivedAttributes.insert( tr( "Time Step" ), layer->formatTime( meta.time() ) );
343  derivedAttributes.insert( tr( "Source" ), groupMeta.uri() );
344 
345  QString resultName = groupMeta.name();
346  if ( isTemporal && ( index.group() == activeScalarGroup || index.group() == activeVectorGroup ) )
347  resultName.append( tr( " (active)" ) );
348 
349  const IdentifyResult result( layer,
350  resultName,
351  attribute,
352  derivedAttributes );
353 
354  results->append( result );
355  }
356 
357  QMap<QString, QString> derivedGeometry;
358 
359  QgsPointXY vertexPoint = layer->snapOnElement( QgsMesh::Vertex, point, searchRadius );
360  if ( !vertexPoint.isEmpty() )
361  {
362  derivedGeometry.insert( tr( "Snapped Vertex Position X" ), QString::number( vertexPoint.x() ) );
363  derivedGeometry.insert( tr( "Snapped Vertex Position Y" ), QString::number( vertexPoint.y() ) );
364  }
365 
366  QgsPointXY faceCentroid = layer->snapOnElement( QgsMesh::Face, point, searchRadius );
367  if ( !faceCentroid.isEmpty() )
368  {
369  derivedGeometry.insert( tr( "Face Centroid X" ), QString::number( faceCentroid.x() ) );
370  derivedGeometry.insert( tr( "Face Centroid Y" ), QString::number( faceCentroid.y() ) );
371  }
372 
373  QgsPointXY pointOnEdge = layer->snapOnElement( QgsMesh::Edge, point, searchRadius );
374  if ( !pointOnEdge.isEmpty() )
375  {
376  derivedGeometry.insert( tr( "Point on Edge X" ), QString::number( pointOnEdge.x() ) );
377  derivedGeometry.insert( tr( "Point on Edge Y" ), QString::number( pointOnEdge.y() ) );
378  }
379 
380  const IdentifyResult result( layer,
381  tr( "Geometry" ),
383  derivedGeometry );
384 
385  results->append( result );
386 
387  return true;
388 }
389 
390 bool QgsMapToolIdentify::identifyVectorTileLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorTileLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
391 {
392  Q_UNUSED( identifyContext )
393  if ( !layer || !layer->isSpatial() )
394  return false;
395 
396  if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
397  {
398  QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
399  return false;
400  }
401 
402  QgsTemporaryCursorOverride waitCursor( Qt::WaitCursor );
403 
404  QMap< QString, QString > commonDerivedAttributes;
405 
406  QgsGeometry selectionGeom = geometry;
407  bool isPointOrRectangle;
408  QgsPointXY point;
409  bool isSingleClick = selectionGeom.type() == QgsWkbTypes::PointGeometry;
410  if ( isSingleClick )
411  {
412  isPointOrRectangle = true;
413  point = selectionGeom.asPoint();
414 
415  commonDerivedAttributes = derivedAttributesForPoint( QgsPoint( point ) );
416  }
417  else
418  {
419  // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
420  isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
421  }
422 
423  int featureCount = 0;
424 
425  std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
426 
427  // toLayerCoordinates will throw an exception for an 'invalid' point.
428  // For example, if you project a world map onto a globe using EPSG 2163
429  // and then click somewhere off the globe, an exception will be thrown.
430  try
431  {
432  QgsRectangle r;
433  if ( isSingleClick )
434  {
435  double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
436  r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
437  }
438  else
439  {
440  r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
441 
442  if ( !isPointOrRectangle )
443  {
445  if ( ct.isValid() )
446  selectionGeom.transform( ct );
447 
448  // use prepared geometry for faster intersection test
449  selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
450  }
451  }
452 
453  int tileZoom = QgsVectorTileUtils::scaleToZoomLevel( mCanvas->scale(), layer->sourceMinZoom(), layer->sourceMaxZoom() );
454  QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( tileZoom );
455  QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
456 
457  for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
458  {
459  for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); ++col )
460  {
461  QgsTileXYZ tileID( col, row, tileZoom );
462  QByteArray data = layer->getRawTile( tileID );
463  if ( data.isEmpty() )
464  continue; // failed to get data
465 
466  QgsVectorTileMVTDecoder decoder;
467  if ( !decoder.decode( tileID, data ) )
468  continue; // failed to decode
469 
470  QMap<QString, QgsFields> perLayerFields;
471  const QStringList layerNames = decoder.layers();
472  for ( const QString &layerName : layerNames )
473  {
474  QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
475  perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
476  }
477 
478  const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
479  const QStringList featuresLayerNames = features.keys();
480  for ( const QString &layerName : featuresLayerNames )
481  {
482  const QgsFields fFields = perLayerFields[layerName];
483  const QVector<QgsFeature> &layerFeatures = features[layerName];
484  for ( const QgsFeature &f : layerFeatures )
485  {
486  if ( f.geometry().intersects( r ) && ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) ) )
487  {
488  QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
489  derivedAttributes.insert( tr( "Feature ID" ), FID_TO_STRING( f.id() ) );
490 
491  results->append( IdentifyResult( layer, layerName, fFields, f, derivedAttributes ) );
492 
493  featureCount++;
494  }
495  }
496  }
497  }
498  }
499 
500  }
501  catch ( QgsCsException &cse )
502  {
503  Q_UNUSED( cse )
504  // catch exception for 'invalid' point and proceed with no features found
505  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
506  }
507 
508  return featureCount > 0;
509 }
510 
511 bool QgsMapToolIdentify::identifyPointCloudLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsPointCloudLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
512 {
513  Q_UNUSED( identifyContext )
514  QgsPointCloudRenderer *renderer = layer->renderer();
515 
518 
519  const double searchRadiusMapUnits = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
520 
521  const QVector<QVariantMap> points = renderer->identify( layer, context, geometry, searchRadiusMapUnits );
522 
523  fromPointCloudIdentificationToIdentifyResults( layer, points, *results );
524 
525  return true;
526 }
527 
528 QMap<QString, QString> QgsMapToolIdentify::derivedAttributesForPoint( const QgsPoint &point )
529 {
530  QMap< QString, QString > derivedAttributes;
531  derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
532  derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
533  if ( point.is3D() )
534  derivedAttributes.insert( tr( "(clicked coordinate Z)" ), QString::number( point.z(), 'f' ) );
535  return derivedAttributes;
536 }
537 
538 bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
539 {
540  if ( !layer || !layer->isSpatial() || !layer->dataProvider() )
541  return false;
542 
543  if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
544  {
545  QgsDebugMsg( QStringLiteral( "Out of scale limits" ) );
546  return false;
547  }
548 
549  QString temporalFilter;
550  if ( identifyContext.isTemporal() )
551  {
552  if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
553  return false;
554 
555  QgsVectorLayerTemporalContext temporalContext;
556  temporalContext.setLayer( layer );
557  temporalFilter = qobject_cast< const QgsVectorLayerTemporalProperties * >( layer->temporalProperties() )->createFilterString( temporalContext, identifyContext.temporalRange() );
558  }
559 
560  const bool fetchFeatureSymbols = layer->dataProvider()->capabilities() & QgsVectorDataProvider::FeatureSymbology;
561 
562  QApplication::setOverrideCursor( Qt::WaitCursor );
563 
564  QMap< QString, QString > commonDerivedAttributes;
565 
566  QgsGeometry selectionGeom = geometry;
567  bool isPointOrRectangle;
568  QgsPointXY point;
569  bool isSingleClick = selectionGeom.type() == QgsWkbTypes::PointGeometry;
570  if ( isSingleClick )
571  {
572  isPointOrRectangle = true;
573  point = selectionGeom.asPoint();
574 
575  commonDerivedAttributes = derivedAttributesForPoint( QgsPoint( point ) );
576  }
577  else
578  {
579  // we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
580  isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
581  }
582 
583  int featureCount = 0;
584 
585  QgsFeatureList featureList;
586  std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
587 
588  // toLayerCoordinates will throw an exception for an 'invalid' point.
589  // For example, if you project a world map onto a globe using EPSG 2163
590  // and then click somewhere off the globe, an exception will be thrown.
591  try
592  {
593  QgsRectangle r;
594  if ( isSingleClick )
595  {
596  double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
597  r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
598  }
599  else
600  {
601  r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
602 
603  if ( !isPointOrRectangle )
604  {
606  if ( ct.isValid() )
607  selectionGeom.transform( ct );
608 
609  // use prepared geometry for faster intersection test
610  selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
611  }
612  }
613 
614  QgsFeatureRequest featureRequest;
615  featureRequest.setFilterRect( r );
616  featureRequest.setFlags( QgsFeatureRequest::ExactIntersect | ( fetchFeatureSymbols ? QgsFeatureRequest::EmbeddedSymbols : QgsFeatureRequest::Flags() ) );
617  if ( !temporalFilter.isEmpty() )
618  featureRequest.setFilterExpression( temporalFilter );
619 
620  QgsFeatureIterator fit = layer->getFeatures( featureRequest );
621  QgsFeature f;
622  while ( fit.nextFeature( f ) )
623  {
624  if ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) )
625  featureList << QgsFeature( f );
626  }
627  }
628  catch ( QgsCsException &cse )
629  {
630  Q_UNUSED( cse )
631  // catch exception for 'invalid' point and proceed with no features found
632  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
633  }
634 
635  bool filter = false;
636 
640  std::unique_ptr< QgsFeatureRenderer > renderer( layer->renderer() ? layer->renderer()->clone() : nullptr );
641  if ( renderer )
642  {
643  // setup scale for scale dependent visibility (rule based)
644  renderer->startRender( context, layer->fields() );
645  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
646  }
647 
648  for ( const QgsFeature &feature : std::as_const( featureList ) )
649  {
650  QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
651 
652  QgsFeatureId fid = feature.id();
653  context.expressionContext().setFeature( feature );
654 
655  if ( filter && !renderer->willRenderFeature( feature, context ) )
656  continue;
657 
658  featureCount++;
659 
660  // When not single click identify, pass an empty point so some derived attributes may still be computed
661  if ( !isSingleClick )
662  point = QgsPointXY();
663  derivedAttributes.unite( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
664 
665  derivedAttributes.insert( tr( "Feature ID" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) );
666 
667  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), feature, derivedAttributes ) );
668  }
669 
670  if ( renderer )
671  {
672  renderer->stopRender( context );
673  }
674 
675  QgsDebugMsgLevel( "Feature count on identify: " + QString::number( featureCount ), 2 );
676 
677  QApplication::restoreOverrideCursor();
678  return featureCount > 0;
679 }
680 
681 void QgsMapToolIdentify::closestVertexAttributes( const QgsAbstractGeometry &geometry, QgsVertexId vId, QgsMapLayer *layer, QMap< QString, QString > &derivedAttributes )
682 {
683  if ( ! vId.isValid( ) )
684  {
685  // We should not get here ...
686  QgsDebugMsg( "Invalid vertex id!" );
687  return;
688  }
689 
690  QString str = QLocale().toString( vId.vertex + 1 );
691  derivedAttributes.insert( tr( "Closest vertex number" ), str );
692 
693  QgsPoint closestPoint = geometry.vertexAt( vId );
694 
695  QgsPointXY closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( closestPoint.x(), closestPoint.y() ) );
696  derivedAttributes.insert( tr( "Closest vertex X" ), formatXCoordinate( closestPointMapCoords ) );
697  derivedAttributes.insert( tr( "Closest vertex Y" ), formatYCoordinate( closestPointMapCoords ) );
698 
699  if ( closestPoint.is3D() )
700  {
701  str = QLocale().toString( closestPoint.z(), 'g', 10 );
702  derivedAttributes.insert( tr( "Closest vertex Z" ), str );
703  }
704  if ( closestPoint.isMeasure() )
705  {
706  str = QLocale().toString( closestPoint.m(), 'g', 10 );
707  derivedAttributes.insert( tr( "Closest vertex M" ), str );
708  }
709 
710  if ( vId.type == QgsVertexId::CurveVertex )
711  {
712  double radius, centerX, centerY;
713  QgsVertexId vIdBefore = vId;
714  --vIdBefore.vertex;
715  QgsVertexId vIdAfter = vId;
716  ++vIdAfter.vertex;
717  QgsGeometryUtils::circleCenterRadius( geometry.vertexAt( vIdBefore ), geometry.vertexAt( vId ),
718  geometry.vertexAt( vIdAfter ), radius, centerX, centerY );
719  derivedAttributes.insert( QStringLiteral( "Closest vertex radius" ), QLocale().toString( radius ) );
720  }
721 }
722 
723 void QgsMapToolIdentify::closestPointAttributes( const QgsAbstractGeometry &geometry, const QgsPointXY &layerPoint, QMap<QString, QString> &derivedAttributes )
724 {
725  QgsPoint closestPoint = QgsGeometryUtils::closestPoint( geometry, QgsPoint( layerPoint ) );
726 
727  derivedAttributes.insert( tr( "Closest X" ), formatXCoordinate( closestPoint ) );
728  derivedAttributes.insert( tr( "Closest Y" ), formatYCoordinate( closestPoint ) );
729 
730  if ( closestPoint.is3D() )
731  {
732  const QString str = QLocale().toString( closestPoint.z(), 'g', 10 );
733  derivedAttributes.insert( tr( "Interpolated Z" ), str );
734  }
735  if ( closestPoint.isMeasure() )
736  {
737  const QString str = QLocale().toString( closestPoint.m(), 'g', 10 );
738  derivedAttributes.insert( tr( "Interpolated M" ), str );
739  }
740 }
741 
742 QString QgsMapToolIdentify::formatCoordinate( const QgsPointXY &canvasPoint ) const
743 {
744  return QgsCoordinateUtils::formatCoordinateForProject( QgsProject::instance(), canvasPoint, mCanvas->mapSettings().destinationCrs(),
745  mCoordinatePrecision );
746 }
747 
748 QString QgsMapToolIdentify::formatXCoordinate( const QgsPointXY &canvasPoint ) const
749 {
750  QString coordinate = formatCoordinate( canvasPoint );
751  return coordinate.split( ',' ).at( 0 );
752 }
753 
754 QString QgsMapToolIdentify::formatYCoordinate( const QgsPointXY &canvasPoint ) const
755 {
756  QString coordinate = formatCoordinate( canvasPoint );
757  return coordinate.split( ',' ).at( 1 );
758 }
759 
760 QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( const QgsFeature &feature, QgsMapLayer *layer, const QgsPointXY &layerPoint )
761 {
762  // Calculate derived attributes and insert:
763  // measure distance or area depending on geometry type
764  QMap< QString, QString > derivedAttributes;
765 
766  // init distance/area calculator
767  QString ellipsoid = QgsProject::instance()->ellipsoid();
768  QgsDistanceArea calc;
769  calc.setEllipsoid( ellipsoid );
770  calc.setSourceCrs( layer->crs(), QgsProject::instance()->transformContext() );
771 
774 
775  QgsVertexId vId;
776  QgsPoint closestPoint;
777  if ( feature.hasGeometry() )
778  {
779  geometryType = feature.geometry().type();
780  wkbType = feature.geometry().wkbType();
781  if ( !layerPoint.isEmpty() )
782  {
783  //find closest vertex to clicked point
784  closestPoint = QgsGeometryUtils::closestVertex( *feature.geometry().constGet(), QgsPoint( layerPoint ), vId );
785  }
786  }
787 
788 
789 
790  if ( QgsWkbTypes::isMultiType( wkbType ) )
791  {
792  QString str = QLocale().toString( static_cast<const QgsGeometryCollection *>( feature.geometry().constGet() )->numGeometries() );
793  derivedAttributes.insert( tr( "Parts" ), str );
794  if ( !layerPoint.isEmpty() )
795  {
796  str = QLocale().toString( vId.part + 1 );
797  derivedAttributes.insert( tr( "Part number" ), str );
798  }
799  }
800 
801  QgsUnitTypes::DistanceUnit cartesianDistanceUnits = QgsUnitTypes::unitType( layer->crs().mapUnits() ) == QgsUnitTypes::unitType( displayDistanceUnits() )
802  ? displayDistanceUnits() : layer->crs().mapUnits();
803  QgsUnitTypes::AreaUnit cartesianAreaUnits = QgsUnitTypes::unitType( QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() ) ) == QgsUnitTypes::unitType( displayAreaUnits() )
804  ? displayAreaUnits() : QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() );
805 
806  if ( geometryType == QgsWkbTypes::LineGeometry )
807  {
808  double dist = calc.measureLength( feature.geometry() );
809  dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
810  QString str;
811  if ( ellipsoid != geoNone() )
812  {
813  str = formatDistance( dist );
814  derivedAttributes.insert( tr( "Length (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
815  }
816  str = formatDistance( feature.geometry().constGet()->length()
817  * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
818  if ( !QgsWkbTypes::hasZ( feature.geometry().wkbType() ) )
819  derivedAttributes.insert( tr( "Length (Cartesian)" ), str );
820  else
821  derivedAttributes.insert( tr( "Length (Cartesian — 2D)" ), str );
823  {
824  str = formatDistance( qgsgeometry_cast< const QgsLineString * >( feature.geometry().constGet() )->length3D()
825  * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
826  derivedAttributes.insert( tr( "Length (Cartesian — 3D)" ), str );
827  }
828 
829  const QgsAbstractGeometry *geom = feature.geometry().constGet();
830  if ( geom )
831  {
832  str = QLocale().toString( geom->nCoordinates() );
833  derivedAttributes.insert( tr( "Vertices" ), str );
834  if ( !layerPoint.isEmpty() )
835  {
836  //add details of closest vertex to identify point
837  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
838  closestPointAttributes( *geom, layerPoint, derivedAttributes );
839  }
840 
841  if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
842  {
843  // Add the start and end points in as derived attributes
844  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->startPoint().x(), curve->startPoint().y() ) );
845  str = formatXCoordinate( pnt );
846  derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
847  str = formatYCoordinate( pnt );
848  derivedAttributes.insert( tr( "firstY" ), str );
849  pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->endPoint().x(), curve->endPoint().y() ) );
850  str = formatXCoordinate( pnt );
851  derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
852  str = formatYCoordinate( pnt );
853  derivedAttributes.insert( tr( "lastY" ), str );
854  }
855  }
856  }
857  else if ( geometryType == QgsWkbTypes::PolygonGeometry )
858  {
859  double area = calc.measureArea( feature.geometry() );
860  area = calc.convertAreaMeasurement( area, displayAreaUnits() );
861  QString str;
862  if ( ellipsoid != geoNone() )
863  {
864  str = formatArea( area );
865  derivedAttributes.insert( tr( "Area (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
866  }
867  str = formatArea( feature.geometry().area()
868  * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::distanceToAreaUnit( layer->crs().mapUnits() ), cartesianAreaUnits ), cartesianAreaUnits );
869  derivedAttributes.insert( tr( "Area (Cartesian)" ), str );
870 
871  if ( ellipsoid != geoNone() )
872  {
873  double perimeter = calc.measurePerimeter( feature.geometry() );
874  perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
875  str = formatDistance( perimeter );
876  derivedAttributes.insert( tr( "Perimeter (Ellipsoidal — %1)" ).arg( ellipsoid ), str );
877  }
878  str = formatDistance( feature.geometry().constGet()->perimeter()
879  * QgsUnitTypes::fromUnitToUnitFactor( layer->crs().mapUnits(), cartesianDistanceUnits ), cartesianDistanceUnits );
880  derivedAttributes.insert( tr( "Perimeter (Cartesian)" ), str );
881 
882  str = QLocale().toString( feature.geometry().constGet()->nCoordinates() );
883  derivedAttributes.insert( tr( "Vertices" ), str );
884 
885  if ( !layerPoint.isEmpty() )
886  {
887  //add details of closest vertex to identify point
888  closestVertexAttributes( *feature.geometry().constGet(), vId, layer, derivedAttributes );
889  closestPointAttributes( *feature.geometry().constGet(), layerPoint, derivedAttributes );
890  }
891  }
892  else if ( geometryType == QgsWkbTypes::PointGeometry )
893  {
894  if ( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point )
895  {
896  // Include the x and y coordinates of the point as a derived attribute
897  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, feature.geometry().asPoint() );
898  QString str = formatXCoordinate( pnt );
899  derivedAttributes.insert( tr( "X" ), str );
900  str = formatYCoordinate( pnt );
901  derivedAttributes.insert( tr( "Y" ), str );
902 
903  if ( QgsWkbTypes::hasZ( wkbType ) )
904  {
905  str = QLocale().toString( static_cast<const QgsPoint *>( feature.geometry().constGet() )->z(), 'g', 10 );
906  derivedAttributes.insert( tr( "Z" ), str );
907  }
908  if ( QgsWkbTypes::hasM( wkbType ) )
909  {
910  str = QLocale().toString( static_cast<const QgsPoint *>( feature.geometry().constGet() )->m(), 'g', 10 );
911  derivedAttributes.insert( tr( "M" ), str );
912  }
913  }
914  else
915  {
916  //multipart
917 
918  if ( !layerPoint.isEmpty() )
919  {
920  //add details of closest vertex to identify point
921  const QgsAbstractGeometry *geom = feature.geometry().constGet();
922  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
923  }
924  }
925  }
926 
927  if ( feature.embeddedSymbol() )
928  {
929  derivedAttributes.insert( tr( "Embedded Symbol" ), tr( "%1 (%2)" ).arg( QgsSymbol::symbolTypeToString( feature.embeddedSymbol()->type() ), feature.embeddedSymbol()->color().name() ) );
930  }
931 
932  return derivedAttributes;
933 }
934 
935 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
936 {
937  QgsPointXY point = geometry.asPoint(); // raster layers currently only support identification by point
938  return identifyRasterLayer( results, layer, point, viewExtent, mapUnitsPerPixel, identifyContext );
939 }
940 
941 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext )
942 {
943  QgsDebugMsg( "point = " + point.toString() );
944  if ( !layer )
945  return false;
946 
947  std::unique_ptr< QgsRasterDataProvider > dprovider( layer->dataProvider()->clone() );
948  if ( !dprovider )
949  return false;
950 
951  int capabilities = dprovider->capabilities();
952  if ( !( capabilities & QgsRasterDataProvider::Identify ) )
953  return false;
954 
955  if ( identifyContext.isTemporal() )
956  {
957  if ( !layer->temporalProperties()->isVisibleInTemporalRange( identifyContext.temporalRange() ) )
958  return false;
959 
960  dprovider->temporalCapabilities()->setRequestedTemporalRange( identifyContext.temporalRange() );
961  }
962 
963  QgsPointXY pointInCanvasCrs = point;
964  try
965  {
966  point = toLayerCoordinates( layer, point );
967  }
968  catch ( QgsCsException &cse )
969  {
970  Q_UNUSED( cse )
971  QgsDebugMsg( QStringLiteral( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
972  return false;
973  }
974  QgsDebugMsg( QStringLiteral( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) );
975 
976  if ( !layer->extent().contains( point ) )
977  return false;
978 
979  QMap< QString, QString > attributes, derivedAttributes;
980 
981  QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
982 
983  // check if the format is really supported otherwise use first supported format
984  if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) )
985  {
987  else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue;
988  else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml;
989  else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText;
990  else return false;
991  }
992 
993  QgsRasterIdentifyResult identifyResult;
994  // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
995  if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
996  {
997  // To get some reasonable response for point/line WMS vector layers we must
998  // use a context with approximately a resolution in layer CRS units
999  // corresponding to current map canvas resolution (for examplei UMN Mapserver
1000  // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
1001  // + TOLERANCE (layer param) for feature selection)
1002  //
1003  QgsRectangle r;
1004  r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
1005  r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
1006  r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
1007  r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
1008  r = toLayerCoordinates( layer, r ); // will be a bit larger
1009  // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
1010  // but that is fixed (the rect is enlarged) in the WMS provider
1011  identifyResult = dprovider->identify( point, format, r, 1, 1 );
1012  }
1013  else
1014  {
1015  // It would be nice to use the same extent and size which was used for drawing,
1016  // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
1017  // is doing some tricks with extent and size to align raster to output which
1018  // would be difficult to replicate here.
1019  // Note: cutting the extent may result in slightly different x and y resolutions
1020  // and thus shifted point calculated back in QGIS WMS (using average resolution)
1021  //viewExtent = dprovider->extent().intersect( &viewExtent );
1022 
1023  // Width and height are calculated from not projected extent and we hope that
1024  // are similar to source width and height used to reproject layer for drawing.
1025  // TODO: may be very dangerous, because it may result in different resolutions
1026  // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
1027  int width = static_cast< int >( std::round( viewExtent.width() / mapUnitsPerPixel ) );
1028  int height = static_cast< int >( std::round( viewExtent.height() / mapUnitsPerPixel ) );
1029 
1030  QgsDebugMsg( QStringLiteral( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) );
1031  QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( width ).arg( height ) );
1032  QgsDebugMsg( QStringLiteral( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) );
1033 
1034  identifyResult = dprovider->identify( point, format, viewExtent, width, height );
1035  }
1036 
1037  derivedAttributes.unite( derivedAttributesForPoint( QgsPoint( pointInCanvasCrs ) ) );
1038 
1039  if ( identifyResult.isValid() )
1040  {
1041  QMap<int, QVariant> values = identifyResult.results();
1042  QgsGeometry geometry;
1043  if ( format == QgsRaster::IdentifyFormatValue )
1044  {
1045  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1046  {
1047  QString valueString;
1048  if ( it.value().isNull() )
1049  {
1050  valueString = tr( "no data" );
1051  }
1052  else
1053  {
1054  QVariant value( it.value() );
1055  // The cast is legit. Quoting QT doc :
1056  // "Although this function is declared as returning QVariant::Type,
1057  // the return value should be interpreted as QMetaType::Type"
1058  if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float )
1059  {
1060  valueString = QgsRasterBlock::printValue( value.toFloat() );
1061  }
1062  else
1063  {
1064  valueString = QgsRasterBlock::printValue( value.toDouble() );
1065  }
1066  }
1067  attributes.insert( dprovider->generateBandName( it.key() ), valueString );
1068  }
1069  QString label = layer->name();
1070  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1071  }
1072  else if ( format == QgsRaster::IdentifyFormatFeature )
1073  {
1074  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1075  {
1076  QVariant value = it.value();
1077  if ( value.type() == QVariant::Bool && !value.toBool() )
1078  {
1079  // sublayer not visible or not queryable
1080  continue;
1081  }
1082 
1083  if ( value.type() == QVariant::String )
1084  {
1085  // error
1086  // TODO: better error reporting
1087  QString label = layer->subLayers().value( it.key() );
1088  attributes.clear();
1089  attributes.insert( tr( "Error" ), value.toString() );
1090 
1091  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1092  continue;
1093  }
1094 
1095  // list of feature stores for a single sublayer
1096  const QgsFeatureStoreList featureStoreList = value.value<QgsFeatureStoreList>();
1097 
1098  for ( const QgsFeatureStore &featureStore : featureStoreList )
1099  {
1100  const QgsFeatureList storeFeatures = featureStore.features();
1101  for ( const QgsFeature &feature : storeFeatures )
1102  {
1103  attributes.clear();
1104  // WMS sublayer and feature type, a sublayer may contain multiple feature types.
1105  // Sublayer name may be the same as layer name and feature type name
1106  // may be the same as sublayer. We try to avoid duplicities in label.
1107  QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
1108  QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
1109  // Strip UMN MapServer '_feature'
1110  featureType.remove( QStringLiteral( "_feature" ) );
1111  QStringList labels;
1112  if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
1113  {
1114  labels << sublayer;
1115  }
1116  if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
1117  {
1118  labels << featureType;
1119  }
1120 
1121  QMap< QString, QString > derAttributes = derivedAttributes;
1122  derAttributes.unite( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
1123 
1124  IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QLatin1String( " / " ) ), featureStore.fields(), feature, derAttributes );
1125 
1126  identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
1127  results->append( identifyResult );
1128  }
1129  }
1130  }
1131  }
1132  else // text or html
1133  {
1134  QgsDebugMsg( QStringLiteral( "%1 HTML or text values" ).arg( values.size() ) );
1135  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
1136  {
1137  QString value = it.value().toString();
1138  attributes.clear();
1139  attributes.insert( QString(), value );
1140 
1141  QString label = layer->subLayers().value( it.key() );
1142  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1143  }
1144  }
1145  }
1146  else
1147  {
1148  attributes.clear();
1149  QString value = identifyResult.error().message( QgsErrorMessage::Text );
1150  attributes.insert( tr( "Error" ), value );
1151  QString label = tr( "Identify error" );
1152  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
1153  }
1154 
1155  return true;
1156 }
1157 
1158 QgsUnitTypes::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
1159 {
1160  return mCanvas->mapUnits();
1161 }
1162 
1163 QgsUnitTypes::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
1164 {
1166 }
1167 
1168 QString QgsMapToolIdentify::formatDistance( double distance ) const
1169 {
1170  return formatDistance( distance, displayDistanceUnits() );
1171 }
1172 
1173 QString QgsMapToolIdentify::formatArea( double area ) const
1174 {
1175  return formatArea( area, displayAreaUnits() );
1176 }
1177 
1178 QString QgsMapToolIdentify::formatDistance( double distance, QgsUnitTypes::DistanceUnit unit ) const
1179 {
1180  QgsSettings settings;
1181  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1182 
1183  return QgsDistanceArea::formatDistance( distance, 3, unit, baseUnit );
1184 }
1185 
1186 QString QgsMapToolIdentify::formatArea( double area, QgsUnitTypes::AreaUnit unit ) const
1187 {
1188  QgsSettings settings;
1189  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
1190 
1191  return QgsDistanceArea::formatArea( area, 3, unit, baseUnit );
1192 }
1193 
1195 {
1196  QList<IdentifyResult> results;
1197  if ( identifyRasterLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel ) )
1198  {
1199  emit changedRasterResults( results );
1200  }
1201 }
1202 
1203 void QgsMapToolIdentify::fromPointCloudIdentificationToIdentifyResults( QgsPointCloudLayer *layer, const QVector<QVariantMap> &identified, QList<QgsMapToolIdentify::IdentifyResult> &results )
1204 {
1205  int id = 1;
1206  const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
1207  for ( const QVariantMap &pt : identified )
1208  {
1209  QMap<QString, QString> ptStr;
1210  QString classification;
1211  for ( auto attrIt = pt.constBegin(); attrIt != pt.constEnd(); ++attrIt )
1212  {
1213  if ( attrIt.key().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) == 0
1214  && ( !qgsDoubleNear( elevationProps->zScale(), 1 ) || !qgsDoubleNear( elevationProps->zOffset(), 0 ) ) )
1215  {
1216  // Apply elevation properties
1217  ptStr[ tr( "Z (original)" ) ] = attrIt.value().toString();
1218  ptStr[ tr( "Z (adjusted)" ) ] = QString::number( attrIt.value().toDouble() * elevationProps->zScale() + elevationProps->zOffset() );
1219  }
1220  else if ( attrIt.key().compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
1221  {
1222  classification = QgsPointCloudDataProvider::translatedLasClassificationCodes().value( attrIt.value().toInt() );
1223  ptStr[ attrIt.key() ] = QStringLiteral( "%1 (%2)" ).arg( attrIt.value().toString(), classification );
1224  }
1225  else
1226  {
1227  ptStr[attrIt.key()] = attrIt.value().toString();
1228  }
1229  }
1230  QgsMapToolIdentify::IdentifyResult res( layer, classification.isEmpty() ? QString::number( id ) : QStringLiteral( "%1 (%2)" ).arg( id ).arg( classification ), ptStr, QMap<QString, QString>() );
1231  results.append( res );
1232  ++id;
1233  }
1234 }
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual double perimeter() const
Returns the planar, 2-dimensional perimeter of the geometry.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
static QString formatDistance(double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
static QString formatArea(double area, int decimals, QgsUnitTypes::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
QString message(QgsErrorMessage::Format format=QgsErrorMessage::Html) const
Full error messages description.
Definition: qgserror.cpp:49
QString what() const
Definition: qgsexception.h:48
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Definition: qgsrenderer.h:264
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ EmbeddedSymbols
Retrieve any embedded feature symbology (since QGIS 3.20)
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
A container for features with the same fields and crs.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Definition: qgsfeature.cpp:288
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY) SIP_HOLDGIL
Returns radius and center of the circle through pt1, pt2, pt3.
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
double area() const
Returns the planar, 2-dimensional area of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
Identify contexts are used to encapsulate the settings to be used to perform an identify action.
bool isTemporal() const
Returns true if the temporal range setting is enabled.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range to be used with the identify action.
The QgsIdentifyMenu class builds a menu to be used with identify results (.
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
QgsUnitTypes::DistanceUnit mapUnits() const
Convenience function for returning the current canvas map units.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
int layerCount() const
Returns number of layers on the map.
double scale() const
Returns the last reported scale of the canvas.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
QgsMapLayer * layer(int index)
Returns the map layer at position index in the layer stack.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
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:70
QString name
Definition: qgsmaplayer.h:73
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
virtual QgsRectangle extent() const
Returns the extent of the layer.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:77
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:76
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:139
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
double scale() const
Returns the calculated map scale.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
QMap< QString, QString > derivedAttributesForPoint(const QgsPoint &point)
Returns derived attributes map for a clicked point in map coordinates. May be 2D or 3D point.
bool identifyLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType=AllLayers, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Call the right method depending on layer type.
static void fromPointCloudIdentificationToIdentifyResults(QgsPointCloudLayer *layer, const QVector< QVariantMap > &identified, QList< QgsMapToolIdentify::IdentifyResult > &results)
Converts point cloud identification results from variant maps to QgsMapToolIdentify::IdentifyResult a...
void changedRasterResults(QList< QgsMapToolIdentify::IdentifyResult > &)
void identifyProgress(int, int)
void deactivate() override
called when map tool is being deactivated
void identifyMessage(const QString &)
void activate() override
called when set as currently active map tool
QList< QgsMapToolIdentify::IdentifyResult > identify(int x, int y, const QList< QgsMapLayer * > &layerList=QList< QgsMapLayer * >(), IdentifyMode mode=DefaultQgsSetting, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification.
void formatChanged(QgsRasterLayer *layer)
void canvasReleaseEvent(QgsMapMouseEvent *e) override
Mouse release event for overriding. Default implementation does nothing.
void setCanvasPropertiesOverrides(double searchRadiusMapUnits)
Overrides some map canvas properties inside the map tool for the upcoming identify requests.
QgsMapToolIdentify(QgsMapCanvas *canvas)
constructor
void canvasMoveEvent(QgsMapMouseEvent *e) override
Mouse move event for overriding. Default implementation does nothing.
bool identifyRasterLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification against a given raster layer.
bool identifyMeshLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsMeshLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Identifies data from active scalar and vector dataset from the mesh layer.
QgsIdentifyMenu * mIdentifyMenu
void canvasPressEvent(QgsMapMouseEvent *e) override
Mouse press event for overriding. Default implementation does nothing.
bool identifyVectorLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsVectorLayer *layer, const QgsPointXY &point, const QgsIdentifyContext &identifyContext=QgsIdentifyContext())
Performs the identification against a given vector layer.
void restoreCanvasPropertiesOverrides()
Clears canvas properties overrides previously set with setCanvasPropertiesOverrides()
Abstract base class for all map tools.
Definition: qgsmaptool.h:66
QgsMapCanvas * mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:297
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
Definition: qgsmaptool.cpp:41
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
Definition: qgsmaptool.cpp:150
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
Transforms a point from screen coordinates to layer coordinates.
Definition: qgsmaptool.cpp:51
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:216
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:72
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:84
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:100
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isTemporal() const
Returns whether the dataset group is temporal (contains time-related dataset)
bool isVector() const
Returns whether dataset group has vector data.
QString name() const
Returns name of the dataset group.
bool isScalar() const
Returns whether dataset group has scalar data.
QString uri() const
Returns the uri of the source.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
QgsMeshDatasetMetadata is a collection of mesh dataset metadata such as whether the data is valid or ...
double time() const
Returns the time value for this dataset.
QgsMeshDatasetValue represents single dataset value.
double y() const
Returns y value.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
double x() const
Returns x value.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
QgsMeshDatasetIndex activeScalarDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active scalar group depending on the time range.
QgsPointXY snapOnElement(QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius)
Returns the position of the snapped point on the mesh element closest to point intersecting with the ...
QList< int > enabledDatasetGroupsIndexes() const
Returns the list of indexes of enables dataset groups handled by the layer.
QgsMeshDatasetIndex staticVectorDatasetIndex() const
Returns the static vector dataset index that is rendered if the temporal properties is not active.
QgsMeshDatasetIndex activeVectorDatasetAtTime(const QgsDateTimeRange &timeRange) const
Returns dataset index from active vector group depending on the time range If the temporal properties...
QgsMeshDatasetIndex staticScalarDatasetIndex() const
Returns the static scalar dataset index that is rendered if the temporal properties is not active.
QgsMeshDatasetMetadata datasetMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset metadata.
QgsMeshDatasetIndex datasetIndexAtTime(const QgsDateTimeRange &timeRange, int datasetGroupIndex) const
Returns dataset index from datasets group depending on the time range.
QgsMeshDatasetValue datasetValue(const QgsMeshDatasetIndex &index, int valueIndex) const
Returns vector/scalar value associated with the index from the dataset To read multiple continuous va...
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
QString formatTime(double hours)
Returns (date) time in hours formatted to human readable form.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
int activeVectorDatasetGroup() const
Returns the active vector dataset group.
int activeScalarDatasetGroup() const
Returns the active scalar dataset group.
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.
double zOffset() const
Returns the z offset, which is a fixed offset amount which should be added to z values from the layer...
double zScale() const
Returns the z scale, which is a scaling factor which should be applied to z values from the layer.
Represents a map layer supporting display of point clouds.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsPointCloudRenderer * renderer()
Returns the 2D renderer for the point cloud.
Abstract base class for 2d point cloud renderers.
QVector< QVariantMap > identify(QgsPointCloudLayer *layer, const QgsRenderContext &context, const QgsGeometry &geometry, double toleranceForPointIdentification=0)
Returns the list of visible points of the point cloud layer layer and an extent defined by a geometry...
virtual void startRender(QgsPointCloudRenderContext &context)
Must be called when a new render cycle is started.
virtual void stopRender(QgsPointCloudRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
A class to represent a 2D point.
Definition: qgspointxy.h:59
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspointxy.h:249
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Definition: qgspointxy.cpp:51
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
QString ellipsoid
Definition: qgsproject.h:106
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
static QString printValue(double value)
Print double value with all necessary significant digits.
static QgsRaster::IdentifyFormat identifyFormatFromName(const QString &formatName)
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
static Capability identifyFormatToCapability(QgsRaster::IdentifyFormat format)
Raster identify results container.
QgsError error() const
Returns the last error.
bool isValid() const
Returns true if valid.
QMap< int, QVariant > results() const
Returns the identify results.
@ IdentifyValue
Numerical values.
@ Identify
At least one identify format supported.
@ IdentifyFeature
WMS GML -> feature.
Represents a raster layer.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
QStringList subLayers() const override
Returns the sublayers of this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
IdentifyFormat
Definition: qgsraster.h:58
@ IdentifyFormatFeature
Definition: qgsraster.h:63
@ IdentifyFormatValue
Definition: qgsraster.h:60
@ IdentifyFormatText
Definition: qgsraster.h:61
@ IdentifyFormatHtml
Definition: qgsraster.h:62
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
bool contains(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:363
Contains information about the context of a rendering operation.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
QgsExpressionContext & expressionContext()
Gets the expression context.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
static QString symbolTypeToString(Qgis::SymbolType type)
Returns a translated string version of the specified symbol type.
Definition: qgssymbol.cpp:230
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:551
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
bool isActive() const
Returns true if the temporal property is active.
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:221
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:103
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent)
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:54
static QgsTileMatrix fromWebMercator(int mZoomLevel)
Returns a tile matrix for the usual web mercator.
Definition: qgstiles.cpp:20
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:66
int endColumn() const
Returns index of the last column in the range.
Definition: qgstiles.h:78
int endRow() const
Returns index of the last row in the range.
Definition: qgstiles.h:82
int startRow() const
Returns index of the first row in the range.
Definition: qgstiles.h:80
int startColumn() const
Returns index of the first column in the range.
Definition: qgstiles.h:76
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:33
Helper functions for various unit types.
Definition: qgsunittypes.h:39
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
static Q_INVOKABLE QgsUnitTypes::AreaUnit distanceToAreaUnit(QgsUnitTypes::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Q_INVOKABLE QgsUnitTypes::DistanceUnitType unitType(QgsUnitTypes::DistanceUnit unit)
Returns the type for a distance unit.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ FeatureSymbology
Provider is able retrieve embedded symbology associated with individual features. Since QGIS 3....
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
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.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns temporal properties associated with the vector layer.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
Implements a map layer that is dedicated to rendering of vector tiles.
QByteArray getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
int sourceMinZoom() const
Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
int sourceMaxZoom() const
Returns maximum zoom level at which source has any valid tiles (negative = unconstrained)
This class is responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
QStringList layerFieldNames(const QString &layerName) const
Returns a list of all field names in a tile. It can only be called after a successful decode()
QStringList layers() const
Returns a list of sub-layer names in a tile. It can only be called after a successful decode()
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct, const QSet< QString > *layerSubset=nullptr) const
Returns decoded features grouped by sub-layers.
bool decode(QgsTileXYZ tileID, const QByteArray &rawTileData)
Tries to decode raw tile data, returns true on success.
static int scaleToZoomLevel(double mapScale, int sourceMinZoom, int sourceMaxZoom)
Finds best fitting zoom level (assuming GoogleCRS84Quad tile matrix set) given map scale denominator ...
static QgsFields makeQgisFields(QSet< QString > flds)
Returns QgsFields instance based on the set of field names.
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:832
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
#define str(x)
Definition: qgis.cpp:37
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:998
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:598
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:736
#define FID_TO_STRING(fid)
Definition: qgsfeatureid.h:33
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QVector< QgsFeatureStore > QgsFeatureStoreList
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)
const QgsCoordinateReferenceSystem & crs
QMap< QString, QVariant > mParams
Utility class for identifying a unique vertex within a geometry.
int vertex
Vertex number.
int part
Part number.
VertexType type
Vertex type.
bool isValid() const SIP_HOLDGIL
Returns true if the vertex id is valid.
@ CurveVertex
An intermediate point on a segment defining the curvature of the segment.