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