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