QGIS API Documentation  3.0.2-Girona (307d082)
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 "qgsidentifymenu.h"
24 #include "qgslogger.h"
25 #include "qgsmapcanvas.h"
26 #include "qgsmaptoolidentify.h"
27 #include "qgsmaptopixel.h"
28 #include "qgsmessageviewer.h"
29 #include "qgsmaplayer.h"
30 #include "qgsrasterdataprovider.h"
31 #include "qgsrasterlayer.h"
34 #include "qgsvectordataprovider.h"
35 #include "qgsvectorlayer.h"
36 #include "qgsproject.h"
37 #include "qgsrenderer.h"
38 #include "qgsgeometryutils.h"
39 #include "qgsgeometrycollection.h"
40 #include "qgscurve.h"
41 #include "qgscoordinateutils.h"
42 #include "qgsexception.h"
43 #include "qgssettings.h"
44 
45 #include <QMouseEvent>
46 #include <QCursor>
47 #include <QPixmap>
48 #include <QStatusBar>
49 #include <QVariant>
50 #include <QMenu>
51 
53  : QgsMapTool( canvas )
54  , mIdentifyMenu( new QgsIdentifyMenu( mCanvas ) )
55  , mLastMapUnitsPerPixel( -1.0 )
56  , mCoordinatePrecision( 6 )
57 {
58  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Identify ) );
59 }
60 
62 {
63  delete mIdentifyMenu;
64 }
65 
67 {
68  Q_UNUSED( e );
69 }
70 
72 {
73  Q_UNUSED( e );
74 }
75 
77 {
78  Q_UNUSED( e );
79 }
80 
81 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, const QList<QgsMapLayer *> &layerList, IdentifyMode mode )
82 {
83  return identify( x, y, mode, layerList, AllLayers );
84 }
85 
86 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, LayerType layerType )
87 {
88  return identify( x, y, mode, QList<QgsMapLayer *>(), layerType );
89 }
90 
91 QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType )
92 {
93  QList<IdentifyResult> results;
94 
95  mLastPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );
96  mLastExtent = mCanvas->extent();
97  mLastMapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
98 
99  mCoordinatePrecision = QgsCoordinateUtils::calculateCoordinatePrecision( mLastMapUnitsPerPixel, mCanvas->mapSettings().destinationCrs() );
100 
101  if ( mode == DefaultQgsSetting )
102  {
103  QgsSettings settings;
104  mode = settings.enumValue( QStringLiteral( "Map/identifyMode" ), ActiveLayer );
105  }
106 
107  if ( mode == LayerSelection )
108  {
109  QList<IdentifyResult> results = identify( x, y, TopDownAll, layerList, layerType );
110  QPoint globalPos = mCanvas->mapToGlobal( QPoint( x + 5, y + 5 ) );
111  return mIdentifyMenu->exec( results, globalPos );
112  }
113  else if ( mode == ActiveLayer && layerList.isEmpty() )
114  {
115  QgsMapLayer *layer = mCanvas->currentLayer();
116 
117  if ( !layer )
118  {
119  emit identifyMessage( tr( "No active layer. To identify features, you must choose an active layer." ) );
120  return results;
121  }
122 
123  QApplication::setOverrideCursor( Qt::WaitCursor );
124 
125  identifyLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel, layerType );
126  }
127  else
128  {
129  QApplication::setOverrideCursor( Qt::WaitCursor );
130 
131  QStringList noIdentifyLayerIdList = QgsProject::instance()->readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
132 
133  int layerCount;
134  if ( layerList.isEmpty() )
135  layerCount = mCanvas->layerCount();
136  else
137  layerCount = layerList.count();
138 
139 
140  for ( int i = 0; i < layerCount; i++ )
141  {
142 
143  QgsMapLayer *layer = nullptr;
144  if ( layerList.isEmpty() )
145  layer = mCanvas->layer( i );
146  else
147  layer = layerList.value( i );
148 
149  emit identifyProgress( i, mCanvas->layerCount() );
150  emit identifyMessage( tr( "Identifying on %1…" ).arg( layer->name() ) );
151 
152  if ( noIdentifyLayerIdList.contains( layer->id() ) )
153  continue;
154 
155  if ( identifyLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel, layerType ) )
156  {
157  if ( mode == TopDownStopAtFirst )
158  break;
159  }
160  }
161 
163  emit identifyMessage( tr( "Identifying done." ) );
164  }
165 
166  QApplication::restoreOverrideCursor();
167 
168  return results;
169 }
170 
172 {
174 }
175 
177 {
179 }
180 
181 bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, LayerType layerType )
182 {
183  if ( layer->type() == QgsMapLayer::RasterLayer && layerType.testFlag( RasterLayer ) )
184  {
185  return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), point, viewExtent, mapUnitsPerPixel );
186  }
187  else if ( layer->type() == QgsMapLayer::VectorLayer && layerType.testFlag( VectorLayer ) )
188  {
189  return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), point );
190  }
191  else
192  {
193  return false;
194  }
195 }
196 
197 bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point )
198 {
199  if ( !layer || !layer->isSpatial() )
200  return false;
201 
202  if ( !layer->isInScaleRange( mCanvas->mapSettings().scale() ) )
203  {
204  QgsDebugMsg( "Out of scale limits" );
205  return false;
206  }
207 
208  QApplication::setOverrideCursor( Qt::WaitCursor );
209 
210  QMap< QString, QString > commonDerivedAttributes;
211 
212  commonDerivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
213  commonDerivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
214 
215  int featureCount = 0;
216 
217  QgsFeatureList featureList;
218 
219  // toLayerCoordinates will throw an exception for an 'invalid' point.
220  // For example, if you project a world map onto a globe using EPSG 2163
221  // and then click somewhere off the globe, an exception will be thrown.
222  try
223  {
224  // create the search rectangle
225  double searchRadius = searchRadiusMU( mCanvas );
226 
227  QgsRectangle r;
228  r.setXMinimum( point.x() - searchRadius );
229  r.setXMaximum( point.x() + searchRadius );
230  r.setYMinimum( point.y() - searchRadius );
231  r.setYMaximum( point.y() + searchRadius );
232 
233  r = toLayerCoordinates( layer, r );
234 
235  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) );
236  QgsFeature f;
237  while ( fit.nextFeature( f ) )
238  featureList << QgsFeature( f );
239  }
240  catch ( QgsCsException &cse )
241  {
242  Q_UNUSED( cse );
243  // catch exception for 'invalid' point and proceed with no features found
244  QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
245  }
246 
247  QgsFeatureList::iterator f_it = featureList.begin();
248 
249  bool filter = false;
250 
253  std::unique_ptr< QgsFeatureRenderer > renderer( layer->renderer() ? layer->renderer()->clone() : nullptr );
254  if ( renderer )
255  {
256  // setup scale for scale dependent visibility (rule based)
257  renderer->startRender( context, layer->fields() );
258  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
259  }
260 
261  for ( ; f_it != featureList.end(); ++f_it )
262  {
263  QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
264 
265  QgsFeatureId fid = f_it->id();
266  context.expressionContext().setFeature( *f_it );
267 
268  if ( filter && !renderer->willRenderFeature( *f_it, context ) )
269  continue;
270 
271  featureCount++;
272 
273  derivedAttributes.unite( featureDerivedAttributes( &( *f_it ), layer, toLayerCoordinates( layer, point ) ) );
274 
275  derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) );
276 
277  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), *f_it, derivedAttributes ) );
278  }
279 
280  if ( renderer )
281  {
282  renderer->stopRender( context );
283  }
284 
285  QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) );
286 
287  QApplication::restoreOverrideCursor();
288  return featureCount > 0;
289 }
290 
291 void QgsMapToolIdentify::closestVertexAttributes( const QgsAbstractGeometry &geometry, QgsVertexId vId, QgsMapLayer *layer, QMap< QString, QString > &derivedAttributes )
292 {
293  QString str = QLocale::system().toString( vId.vertex + 1 );
294  derivedAttributes.insert( tr( "Closest vertex number" ), str );
295 
296  QgsPoint closestPoint = geometry.vertexAt( vId );
297 
298  QgsPointXY closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( closestPoint.x(), closestPoint.y() ) );
299  derivedAttributes.insert( QStringLiteral( "Closest vertex X" ), formatXCoordinate( closestPointMapCoords ) );
300  derivedAttributes.insert( QStringLiteral( "Closest vertex Y" ), formatYCoordinate( closestPointMapCoords ) );
301 
302  if ( closestPoint.is3D() )
303  {
304  str = QLocale::system().toString( closestPoint.z(), 'g', 10 );
305  derivedAttributes.insert( QStringLiteral( "Closest vertex Z" ), str );
306  }
307  if ( closestPoint.isMeasure() )
308  {
309  str = QLocale::system().toString( closestPoint.m(), 'g', 10 );
310  derivedAttributes.insert( QStringLiteral( "Closest vertex M" ), str );
311  }
312 
313  if ( vId.type == QgsVertexId::CurveVertex )
314  {
315  double radius, centerX, centerY;
316  QgsVertexId vIdBefore = vId;
317  --vIdBefore.vertex;
318  QgsVertexId vIdAfter = vId;
319  ++vIdAfter.vertex;
320  QgsGeometryUtils::circleCenterRadius( geometry.vertexAt( vIdBefore ), geometry.vertexAt( vId ),
321  geometry.vertexAt( vIdAfter ), radius, centerX, centerY );
322  derivedAttributes.insert( QStringLiteral( "Closest vertex radius" ), QLocale::system().toString( radius ) );
323  }
324 }
325 
326 QString QgsMapToolIdentify::formatCoordinate( const QgsPointXY &canvasPoint ) const
327 {
328  return QgsCoordinateUtils::formatCoordinateForProject( QgsProject::instance(), canvasPoint, mCanvas->mapSettings().destinationCrs(),
329  mCoordinatePrecision );
330 }
331 
332 QString QgsMapToolIdentify::formatXCoordinate( const QgsPointXY &canvasPoint ) const
333 {
334  QString coordinate = formatCoordinate( canvasPoint );
335  return coordinate.split( ',' ).at( 0 );
336 }
337 
338 QString QgsMapToolIdentify::formatYCoordinate( const QgsPointXY &canvasPoint ) const
339 {
340  QString coordinate = formatCoordinate( canvasPoint );
341  return coordinate.split( ',' ).at( 1 );
342 }
343 
344 QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( QgsFeature *feature, QgsMapLayer *layer, const QgsPointXY &layerPoint )
345 {
346  // Calculate derived attributes and insert:
347  // measure distance or area depending on geometry type
348  QMap< QString, QString > derivedAttributes;
349 
350  // init distance/area calculator
351  QString ellipsoid = QgsProject::instance()->ellipsoid();
352  QgsDistanceArea calc;
353  calc.setEllipsoid( ellipsoid );
354  calc.setSourceCrs( layer->crs(), QgsProject::instance()->transformContext() );
355 
358 
359  QgsVertexId vId;
360  QgsPoint closestPoint;
361  if ( feature->hasGeometry() )
362  {
363  geometryType = feature->geometry().type();
364  wkbType = feature->geometry().wkbType();
365  //find closest vertex to clicked point
366  closestPoint = QgsGeometryUtils::closestVertex( *feature->geometry().constGet(), QgsPoint( layerPoint.x(), layerPoint.y() ), vId );
367  }
368 
369  if ( QgsWkbTypes::isMultiType( wkbType ) )
370  {
371  QString str = QLocale::system().toString( static_cast<const QgsGeometryCollection *>( feature->geometry().constGet() )->numGeometries() );
372  derivedAttributes.insert( tr( "Parts" ), str );
373  str = QLocale::system().toString( vId.part + 1 );
374  derivedAttributes.insert( tr( "Part number" ), str );
375  }
376 
377  if ( geometryType == QgsWkbTypes::LineGeometry )
378  {
379  double dist = calc.measureLength( feature->geometry() );
380  dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
381  QString str = formatDistance( dist );
382  derivedAttributes.insert( tr( "Length" ), str );
383 
384  const QgsAbstractGeometry *geom = feature->geometry().constGet();
385  if ( geom )
386  {
387  str = QLocale::system().toString( geom->nCoordinates() );
388  derivedAttributes.insert( tr( "Vertices" ), str );
389  //add details of closest vertex to identify point
390  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
391 
392  if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
393  {
394  // Add the start and end points in as derived attributes
395  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->startPoint().x(), curve->startPoint().y() ) );
396  str = formatXCoordinate( pnt );
397  derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
398  str = formatYCoordinate( pnt );
399  derivedAttributes.insert( tr( "firstY" ), str );
400  pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPointXY( curve->endPoint().x(), curve->endPoint().y() ) );
401  str = formatXCoordinate( pnt );
402  derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
403  str = formatYCoordinate( pnt );
404  derivedAttributes.insert( tr( "lastY" ), str );
405  }
406  }
407  }
408  else if ( geometryType == QgsWkbTypes::PolygonGeometry )
409  {
410  double area = calc.measureArea( feature->geometry() );
411  area = calc.convertAreaMeasurement( area, displayAreaUnits() );
412  QString str = formatArea( area );
413  derivedAttributes.insert( tr( "Area" ), str );
414 
415  double perimeter = calc.measurePerimeter( feature->geometry() );
416  perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
417  str = formatDistance( perimeter );
418  derivedAttributes.insert( tr( "Perimeter" ), str );
419 
420  str = QLocale::system().toString( feature->geometry().constGet()->nCoordinates() );
421  derivedAttributes.insert( tr( "Vertices" ), str );
422 
423  //add details of closest vertex to identify point
424  closestVertexAttributes( *feature->geometry().constGet(), vId, layer, derivedAttributes );
425  }
426  else if ( geometryType == QgsWkbTypes::PointGeometry )
427  {
428  if ( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point )
429  {
430  // Include the x and y coordinates of the point as a derived attribute
431  QgsPointXY pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, feature->geometry().asPoint() );
432  QString str = formatXCoordinate( pnt );
433  derivedAttributes.insert( QStringLiteral( "X" ), str );
434  str = formatYCoordinate( pnt );
435  derivedAttributes.insert( QStringLiteral( "Y" ), str );
436 
437  if ( QgsWkbTypes::hasZ( wkbType ) )
438  {
439  str = QLocale::system().toString( static_cast<const QgsPoint *>( feature->geometry().constGet() )->z(), 'g', 10 );
440  derivedAttributes.insert( QStringLiteral( "Z" ), str );
441  }
442  if ( QgsWkbTypes::hasM( wkbType ) )
443  {
444  str = QLocale::system().toString( static_cast<const QgsPoint *>( feature->geometry().constGet() )->m(), 'g', 10 );
445  derivedAttributes.insert( QStringLiteral( "M" ), str );
446  }
447  }
448  else
449  {
450  //multipart
451 
452  //add details of closest vertex to identify point
453  const QgsAbstractGeometry *geom = feature->geometry().constGet();
454  {
455  closestVertexAttributes( *geom, vId, layer, derivedAttributes );
456  }
457  }
458  }
459 
460  return derivedAttributes;
461 }
462 
463 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel )
464 {
465  QgsDebugMsg( "point = " + point.toString() );
466  if ( !layer )
467  return false;
468 
469  QgsRasterDataProvider *dprovider = layer->dataProvider();
470  if ( !dprovider )
471  return false;
472 
473  int capabilities = dprovider->capabilities();
474  if ( !( capabilities & QgsRasterDataProvider::Identify ) )
475  return false;
476 
477  QgsPointXY pointInCanvasCrs = point;
478  try
479  {
480  point = toLayerCoordinates( layer, point );
481  }
482  catch ( QgsCsException &cse )
483  {
484  Q_UNUSED( cse );
485  QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
486  return false;
487  }
488  QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) );
489 
490  if ( !layer->extent().contains( point ) )
491  return false;
492 
493  QMap< QString, QString > attributes, derivedAttributes;
494 
495  QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
496 
497  // check if the format is really supported otherwise use first supported format
498  if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) )
499  {
501  else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue;
502  else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml;
503  else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText;
504  else return false;
505  }
506 
507  QgsRasterIdentifyResult identifyResult;
508  // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
509  if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
510  {
511  // To get some reasonable response for point/line WMS vector layers we must
512  // use a context with approximately a resolution in layer CRS units
513  // corresponding to current map canvas resolution (for examplei UMN Mapserver
514  // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
515  // + TOLERANCE (layer param) for feature selection)
516  //
517  QgsRectangle r;
518  r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
519  r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
520  r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
521  r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
522  r = toLayerCoordinates( layer, r ); // will be a bit larger
523  // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
524  // but that is fixed (the rect is enlarged) in the WMS provider
525  identifyResult = dprovider->identify( point, format, r, 1, 1 );
526  }
527  else
528  {
529  // It would be nice to use the same extent and size which was used for drawing,
530  // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
531  // is doing some tricks with extent and size to align raster to output which
532  // would be difficult to replicate here.
533  // Note: cutting the extent may result in slightly different x and y resolutions
534  // and thus shifted point calculated back in QGIS WMS (using average resolution)
535  //viewExtent = dprovider->extent().intersect( &viewExtent );
536 
537  // Width and height are calculated from not projected extent and we hope that
538  // are similar to source width and height used to reproject layer for drawing.
539  // TODO: may be very dangerous, because it may result in different resolutions
540  // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
541  int width = std::round( viewExtent.width() / mapUnitsPerPixel );
542  int height = std::round( viewExtent.height() / mapUnitsPerPixel );
543 
544  QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) );
545  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) );
546  QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) );
547 
548  identifyResult = dprovider->identify( point, format, viewExtent, width, height );
549  }
550 
551  derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) );
552  derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) );
553 
554  if ( identifyResult.isValid() )
555  {
556  QMap<int, QVariant> values = identifyResult.results();
557  QgsGeometry geometry;
558  if ( format == QgsRaster::IdentifyFormatValue )
559  {
560  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
561  {
562  QString valueString;
563  if ( it.value().isNull() )
564  {
565  valueString = tr( "no data" );
566  }
567  else
568  {
569  QVariant value( it.value() );
570  // The cast is legit. Quoting QT doc :
571  // "Although this function is declared as returning QVariant::Type,
572  // the return value should be interpreted as QMetaType::Type"
573  if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float )
574  {
575  valueString = QgsRasterBlock::printValue( value.toFloat() );
576  }
577  else
578  {
579  valueString = QgsRasterBlock::printValue( value.toDouble() );
580  }
581  }
582  attributes.insert( dprovider->generateBandName( it.key() ), valueString );
583  }
584  QString label = layer->name();
585  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
586  }
587  else if ( format == QgsRaster::IdentifyFormatFeature )
588  {
589  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
590  {
591  QVariant value = it.value();
592  if ( value.type() == QVariant::Bool && !value.toBool() )
593  {
594  // sublayer not visible or not queryable
595  continue;
596  }
597 
598  if ( value.type() == QVariant::String )
599  {
600  // error
601  // TODO: better error reporting
602  QString label = layer->subLayers().value( it.key() );
603  attributes.clear();
604  attributes.insert( tr( "Error" ), value.toString() );
605 
606  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
607  continue;
608  }
609 
610  // list of feature stores for a single sublayer
611  const QgsFeatureStoreList featureStoreList = it.value().value<QgsFeatureStoreList>();
612 
613  for ( const QgsFeatureStore &featureStore : featureStoreList )
614  {
615  const QgsFeatureList storeFeatures = featureStore.features();
616  for ( QgsFeature feature : storeFeatures )
617  {
618  attributes.clear();
619  // WMS sublayer and feature type, a sublayer may contain multiple feature types.
620  // Sublayer name may be the same as layer name and feature type name
621  // may be the same as sublayer. We try to avoid duplicities in label.
622  QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
623  QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
624  // Strip UMN MapServer '_feature'
625  featureType.remove( QStringLiteral( "_feature" ) );
626  QStringList labels;
627  if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
628  {
629  labels << sublayer;
630  }
631  if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
632  {
633  labels << featureType;
634  }
635 
636  QMap< QString, QString > derAttributes = derivedAttributes;
637  derAttributes.unite( featureDerivedAttributes( &feature, layer ) );
638 
639  IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QStringLiteral( " / " ) ), featureStore.fields(), feature, derAttributes );
640 
641  identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
642  results->append( identifyResult );
643  }
644  }
645  }
646  }
647  else // text or html
648  {
649  QgsDebugMsg( QString( "%1 HTML or text values" ).arg( values.size() ) );
650  for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
651  {
652  QString value = it.value().toString();
653  attributes.clear();
654  attributes.insert( QLatin1String( "" ), value );
655 
656  QString label = layer->subLayers().value( it.key() );
657  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
658  }
659  }
660  }
661  else
662  {
663  attributes.clear();
664  QString value = identifyResult.error().message( QgsErrorMessage::Text );
665  attributes.insert( tr( "Error" ), value );
666  QString label = tr( "Identify error" );
667  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
668  }
669 
670  return true;
671 }
672 
673 QgsUnitTypes::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
674 {
675  return mCanvas->mapUnits();
676 }
677 
678 QgsUnitTypes::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
679 {
681 }
682 
683 QString QgsMapToolIdentify::formatDistance( double distance ) const
684 {
685  QgsSettings settings;
686  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
687 
688  return QgsDistanceArea::formatDistance( distance, 3, displayDistanceUnits(), baseUnit );
689 }
690 
691 QString QgsMapToolIdentify::formatArea( double area ) const
692 {
693  QgsSettings settings;
694  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
695 
696  return QgsDistanceArea::formatArea( area, 3, displayAreaUnits(), baseUnit );
697 }
698 
700 {
701  QList<IdentifyResult> results;
702  if ( identifyRasterLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel ) )
703  {
704  emit changedRasterResults( results );
705  }
706 }
707 
bool isMeasure() const
Returns true if the geometry contains m values.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection, bool flag=false) const
Return the setting value for a setting based on an enum.
Definition: qgssettings.h:231
Wrapper for iterator of features from vector data provider or vector layer.
A container for features with the same fields and crs.
bool contains(const QgsRectangle &rect) const
Return true when rectangle contains other rectangle.
IdentifyFormat
Definition: qgsraster.h:57
A rectangle specified with double values.
Definition: qgsrectangle.h:39
Base class for all map layer types.
Definition: qgsmaplayer.h:56
double y
Definition: qgspoint.h:42
void changedRasterResults(QList< QgsMapToolIdentify::IdentifyResult > &)
QStringList subLayers() const override
Returns the sublayers of this layer - Useful for providers that manage their own layers, such as WMS.
static QString printValue(double value)
Print double value with all necessary significant digits.
static double searchRadiusMU(const QgsRenderContext &context)
Get search radius in map units for given context.
Definition: qgsmaptool.cpp:206
void activate() override
called when set as currently active map tool
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:94
Use exact geometry intersection (slower) instead of bounding boxes.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:57
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:557
void identifyProgress(int, int)
bool identifyLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType=AllLayers)
Call the right method depending on layer type.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:241
QList< QgsFeatureStore > QgsFeatureStoreList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:549
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:52
int layerCount() const
return number of layers on the map
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
void canvasMoveEvent(QgsMapMouseEvent *e) override
Mouse move event for overriding. Default implementation does nothing.
static Capability identifyFormatToCapability(QgsRaster::IdentifyFormat format)
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
void canvasReleaseEvent(QgsMapMouseEvent *e) override
Mouse release event for overriding. Default implementation does nothing.
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Definition: qgspointxy.cpp:45
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
bool identifyRasterLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
QString ellipsoid
Definition: qgsproject.h:89
void identifyMessage(const QString &)
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
QgsMapLayer::LayerType type() const
Returns the type of the layer.
QgsMapToolIdentify(QgsMapCanvas *canvas)
constructor
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:74
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
Definition: qgsproject.cpp:474
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString what() const
Definition: qgsexception.h:48
Raster identify results container.
virtual void activate()
called when set as currently active map tool
Definition: qgsmaptool.cpp:81
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:236
QgsIdentifyMenu * mIdentifyMenu
QgsRasterDataProvider * dataProvider() override
void formatChanged(QgsRasterLayer *layer)
bool isValid() const
Returns true if valid.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
void deactivate() override
called when map tool is being deactivated
QgsFields fields() const override
Returns the list of fields of this layer.
Utility class for identifying a unique vertex within a geometry.
static QgsRaster::IdentifyFormat identifyFormatFromName(const QString &formatName)
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
QList< QgsMapToolIdentify::IdentifyResult > identify(int x, int y, const QList< QgsMapLayer *> &layerList=QList< QgsMapLayer *>(), IdentifyMode mode=DefaultQgsSetting)
Performs the identification.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
double scale() const
Returns the calculated map scale.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:99
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsFeatureRenderer * renderer()
Return renderer.
The QgsIdentifyMenu class builds a menu to be used with identify results (.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:97
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
Abstract base class for all geometries.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
bool identifyVectorLayer(QList< QgsMapToolIdentify::IdentifyResult > *results, QgsVectorLayer *layer, const QgsPointXY &point)
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double x
Definition: qgspointxy.h:47
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
QMap< int, QVariant > results() const
Get results.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QgsExpressionContext & expressionContext()
Gets the expression context.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:43
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:137
Abstract base class for all map tools.
Definition: qgsmaptool.h:63
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
QgsUnitTypes::DistanceUnit mapUnits() const
Convience function for returning the current canvas map units.
Contains information about the context of a rendering operation.
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
transformation from screen coordinates to layer&#39;s coordinates
Definition: qgsmaptool.cpp:52
QString message(QgsErrorMessage::Format format=QgsErrorMessage::Html) const
Full error messages description.
Definition: qgserror.cpp:49
QgsPointXY asPoint() const
Returns contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
void canvasPressEvent(QgsMapMouseEvent *e) override
Mouse press event for overriding. Default implementation does nothing.
QgsError error() const
Get error.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:104
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
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 void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
static QString formatArea(double area, int decimals, QgsUnitTypes::AreaUnit unit, bool keepBaseUnit=false)
Returns an area formatted as a friendly string.
const QgsMapToPixel * getCoordinateTransform()
Get the current coordinate transform.
qint64 QgsFeatureId
Definition: qgsfeature.h:37
double z
Definition: qgspoint.h:43
virtual void setCursor(const QCursor &cursor)
Set a user defined cursor.
Definition: qgsmaptool.cpp:142
QString name
Definition: qgsmaplayer.h:60
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
virtual QString generateBandName(int bandNumber) const
helper function to create zero padded band names
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:427
static Q_INVOKABLE QgsUnitTypes::AreaUnit distanceToAreaUnit(QgsUnitTypes::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.
AreaUnit
Units of area.
Definition: qgsunittypes.h:69
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
QgsPointXY toMapCoordinates(int x, int y) const
QgsMapLayer * layer(int index)
return the map layer at position index in the layer stack
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:89
double m
Definition: qgspoint.h:44
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149
Base class for raster data providers.
static QString formatDistance(double distance, int decimals, QgsUnitTypes::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
double x
Definition: qgspoint.h:41