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