QGIS API Documentation  2.99.0-Master (9ed189e)
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 
45 #include <QSettings>
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  QSettings 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 QgsPoint& 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 QgsPoint& 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  QgsPointV2 closestPoint = geometry.vertexAt( vId );
300 
301  QgsPoint closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( 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 QString QgsMapToolIdentify::formatCoordinate( const QgsPoint& canvasPoint ) const
330 {
331  return QgsCoordinateUtils::formatCoordinateForProject( canvasPoint, mCanvas->mapSettings().destinationCrs(),
332  mCoordinatePrecision );
333 }
334 
335 QString QgsMapToolIdentify::formatXCoordinate( const QgsPoint& canvasPoint ) const
336 {
337  QString coordinate = formatCoordinate( canvasPoint );
338  return coordinate.split( ',' ).at( 0 );
339 }
340 
341 QString QgsMapToolIdentify::formatYCoordinate( const QgsPoint& canvasPoint ) const
342 {
343  QString coordinate = formatCoordinate( canvasPoint );
344  return coordinate.split( ',' ).at( 1 );
345 }
346 
347 QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( QgsFeature *feature, QgsMapLayer *layer, const QgsPoint& layerPoint )
348 {
349  // Calculate derived attributes and insert:
350  // measure distance or area depending on geometry type
351  QMap< QString, QString > derivedAttributes;
352 
353  // init distance/area calculator
354  QString ellipsoid = QgsProject::instance()->ellipsoid();
355  QgsDistanceArea calc;
356  calc.setEllipsoidalMode( true );
357  calc.setEllipsoid( ellipsoid );
358  calc.setSourceCrs( layer->crs().srsid() );
359 
362 
363  QgsVertexId vId;
364  QgsPointV2 closestPoint;
365  if ( feature->hasGeometry() )
366  {
367  geometryType = feature->geometry().type();
368  wkbType = feature->geometry().geometry()->wkbType();
369  //find closest vertex to clicked point
370  closestPoint = QgsGeometryUtils::closestVertex( *feature->geometry().geometry(), QgsPointV2( layerPoint.x(), layerPoint.y() ), vId );
371  }
372 
373  if ( QgsWkbTypes::isMultiType( wkbType ) )
374  {
375  QString str = QLocale::system().toString( static_cast<const QgsGeometryCollection*>( feature->geometry().geometry() )->numGeometries() );
376  derivedAttributes.insert( tr( "Parts" ), str );
377  str = QLocale::system().toString( vId.part + 1 );
378  derivedAttributes.insert( tr( "Part number" ), str );
379  }
380 
381  if ( geometryType == QgsWkbTypes::LineGeometry )
382  {
383  double dist = calc.measureLength( feature->geometry() );
384  dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
385  QString str = formatDistance( dist );
386  derivedAttributes.insert( tr( "Length" ), str );
387 
388  const QgsCurve* curve = dynamic_cast< const QgsCurve* >( feature->geometry().geometry() );
389  if ( curve )
390  {
391  str = QLocale::system().toString( curve->nCoordinates() );
392  derivedAttributes.insert( tr( "Vertices" ), str );
393 
394  //add details of closest vertex to identify point
395  closestVertexAttributes( *curve, vId, layer, derivedAttributes );
396 
397  // Add the start and end points in as derived attributes
398  QgsPoint pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( curve->startPoint().x(), curve->startPoint().y() ) );
399  str = formatXCoordinate( pnt );
400  derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
401  str = formatYCoordinate( pnt );
402  derivedAttributes.insert( tr( "firstY" ), str );
403  pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( curve->endPoint().x(), curve->endPoint().y() ) );
404  str = formatXCoordinate( pnt );
405  derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
406  str = formatYCoordinate( pnt );
407  derivedAttributes.insert( tr( "lastY" ), str );
408  }
409  }
410  else if ( geometryType == QgsWkbTypes::PolygonGeometry )
411  {
412  double area = calc.measureArea( feature->geometry() );
413  area = calc.convertAreaMeasurement( area, displayAreaUnits() );
414  QString str = formatArea( area );
415  derivedAttributes.insert( tr( "Area" ), str );
416 
417  double perimeter = calc.measurePerimeter( feature->geometry() );
418  perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
419  str = formatDistance( perimeter );
420  derivedAttributes.insert( tr( "Perimeter" ), str );
421 
422  str = QLocale::system().toString( feature->geometry().geometry()->nCoordinates() );
423  derivedAttributes.insert( tr( "Vertices" ), str );
424 
425  //add details of closest vertex to identify point
426  closestVertexAttributes( *feature->geometry().geometry(), vId, layer, derivedAttributes );
427  }
428  else if ( geometryType == QgsWkbTypes::PointGeometry &&
430  {
431  // Include the x and y coordinates of the point as a derived attribute
432  QgsPoint pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, feature->geometry().asPoint() );
433  QString str = formatXCoordinate( pnt );
434  derivedAttributes.insert( QStringLiteral( "X" ), str );
435  str = formatYCoordinate( pnt );
436  derivedAttributes.insert( QStringLiteral( "Y" ), str );
437 
438  if ( QgsWkbTypes::hasZ( wkbType ) )
439  {
440  str = QLocale::system().toString( static_cast<const QgsPointV2*>( feature->geometry().geometry() )->z(), 'g', 10 );
441  derivedAttributes.insert( QStringLiteral( "Z" ), str );
442  }
443  if ( QgsWkbTypes::hasM( wkbType ) )
444  {
445  str = QLocale::system().toString( static_cast<const QgsPointV2*>( feature->geometry().geometry() )->m(), 'g', 10 );
446  derivedAttributes.insert( QStringLiteral( "M" ), str );
447  }
448  }
449 
450  return derivedAttributes;
451 }
452 
453 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle& viewExtent, double mapUnitsPerPixel )
454 {
455  QgsDebugMsg( "point = " + point.toString() );
456  if ( !layer )
457  return false;
458 
459  QgsRasterDataProvider *dprovider = layer->dataProvider();
460  if ( !dprovider )
461  return false;
462 
463  int capabilities = dprovider->capabilities();
464  if ( !( capabilities & QgsRasterDataProvider::Identify ) )
465  return false;
466 
467  QgsPoint pointInCanvasCrs = point;
468  try
469  {
470  point = toLayerCoordinates( layer, point );
471  }
472  catch ( QgsCsException &cse )
473  {
474  Q_UNUSED( cse );
475  QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
476  return false;
477  }
478  QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) );
479 
480  if ( !layer->extent().contains( point ) )
481  return false;
482 
483  QMap< QString, QString > attributes, derivedAttributes;
484 
485  QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
486 
487  // check if the format is really supported otherwise use first supported format
488  if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) )
489  {
491  else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue;
492  else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml;
493  else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText;
494  else return false;
495  }
496 
497  QgsRasterIdentifyResult identifyResult;
498  // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
499  if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
500  {
501  // To get some reasonable response for point/line WMS vector layers we must
502  // use a context with approximately a resolution in layer CRS units
503  // corresponding to current map canvas resolution (for examplei UMN Mapserver
504  // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
505  // + TOLERANCE (layer param) for feature selection)
506  //
507  QgsRectangle r;
508  r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
509  r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
510  r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
511  r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
512  r = toLayerCoordinates( layer, r ); // will be a bit larger
513  // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
514  // but that is fixed (the rect is enlarged) in the WMS provider
515  identifyResult = dprovider->identify( point, format, r, 1, 1 );
516  }
517  else
518  {
519  // It would be nice to use the same extent and size which was used for drawing,
520  // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
521  // is doing some tricks with extent and size to align raster to output which
522  // would be difficult to replicate here.
523  // Note: cutting the extent may result in slightly different x and y resolutions
524  // and thus shifted point calculated back in QGIS WMS (using average resolution)
525  //viewExtent = dprovider->extent().intersect( &viewExtent );
526 
527  // Width and height are calculated from not projected extent and we hope that
528  // are similar to source width and height used to reproject layer for drawing.
529  // TODO: may be very dangerous, because it may result in different resolutions
530  // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
531  int width = qRound( viewExtent.width() / mapUnitsPerPixel );
532  int height = qRound( viewExtent.height() / mapUnitsPerPixel );
533 
534  QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) );
535  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) );
536  QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) );
537 
538  identifyResult = dprovider->identify( point, format, viewExtent, width, height );
539  }
540 
541  derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) );
542  derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) );
543 
544  if ( identifyResult.isValid() )
545  {
546  QMap<int, QVariant> values = identifyResult.results();
547  QgsGeometry geometry;
548  if ( format == QgsRaster::IdentifyFormatValue )
549  {
550  Q_FOREACH ( int bandNo, values.keys() )
551  {
552  QString valueString;
553  if ( values.value( bandNo ).isNull() )
554  {
555  valueString = tr( "no data" );
556  }
557  else
558  {
559  QVariant value( values.value( bandNo ) );
560  // The cast is legit. Quoting QT doc :
561  // "Although this function is declared as returning QVariant::Type,
562  // the return value should be interpreted as QMetaType::Type"
563  if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float )
564  {
565  valueString = QgsRasterBlock::printValue( value.toFloat() );
566  }
567  else
568  {
569  valueString = QgsRasterBlock::printValue( value.toDouble() );
570  }
571  }
572  attributes.insert( dprovider->generateBandName( bandNo ), valueString );
573  }
574  QString label = layer->name();
575  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
576  }
577  else if ( format == QgsRaster::IdentifyFormatFeature )
578  {
579  Q_FOREACH ( int i, values.keys() )
580  {
581  QVariant value = values.value( i );
582  if ( value.type() == QVariant::Bool && !value.toBool() )
583  {
584  // sublayer not visible or not queryable
585  continue;
586  }
587 
588  if ( value.type() == QVariant::String )
589  {
590  // error
591  // TODO: better error reporting
592  QString label = layer->subLayers().value( i );
593  attributes.clear();
594  attributes.insert( tr( "Error" ), value.toString() );
595 
596  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
597  continue;
598  }
599 
600  // list of feature stores for a single sublayer
601  QgsFeatureStoreList featureStoreList = values.value( i ).value<QgsFeatureStoreList>();
602 
603  Q_FOREACH ( QgsFeatureStore featureStore, featureStoreList )
604  {
605  Q_FOREACH ( QgsFeature feature, featureStore.features() )
606  {
607  attributes.clear();
608  // WMS sublayer and feature type, a sublayer may contain multiple feature types.
609  // Sublayer name may be the same as layer name and feature type name
610  // may be the same as sublayer. We try to avoid duplicities in label.
611  QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
612  QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
613  // Strip UMN MapServer '_feature'
614  featureType.remove( QStringLiteral( "_feature" ) );
615  QStringList labels;
616  if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
617  {
618  labels << sublayer;
619  }
620  if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
621  {
622  labels << featureType;
623  }
624 
625  QMap< QString, QString > derAttributes = derivedAttributes;
626  derAttributes.unite( featureDerivedAttributes( &feature, layer ) );
627 
628  IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QStringLiteral( " / " ) ), featureStore.fields(), feature, derAttributes );
629 
630  identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
631  results->append( identifyResult );
632  }
633  }
634  }
635  }
636  else // text or html
637  {
638  QgsDebugMsg( QString( "%1 HTML or text values" ).arg( values.size() ) );
639  Q_FOREACH ( int bandNo, values.keys() )
640  {
641  QString value = values.value( bandNo ).toString();
642  attributes.clear();
643  attributes.insert( QLatin1String( "" ), value );
644 
645  QString label = layer->subLayers().value( bandNo );
646  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
647  }
648  }
649  }
650  else
651  {
652  attributes.clear();
653  QString value = identifyResult.error().message( QgsErrorMessage::Text );
654  attributes.insert( tr( "Error" ), value );
655  QString label = tr( "Identify error" );
656  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
657  }
658 
659  return true;
660 }
661 
662 QgsUnitTypes::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
663 {
664  return mCanvas->mapUnits();
665 }
666 
667 QgsUnitTypes::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
668 {
670 }
671 
672 QString QgsMapToolIdentify::formatDistance( double distance ) const
673 {
674  QSettings settings;
675  bool baseUnit = settings.value( QStringLiteral( "/qgis/measure/keepbaseunit" ), true ).toBool();
676 
677  return QgsDistanceArea::formatDistance( distance, 3, displayDistanceUnits(), baseUnit );
678 }
679 
680 QString QgsMapToolIdentify::formatArea( double area ) const
681 {
682  QSettings settings;
683  bool baseUnit = settings.value( QStringLiteral( "/qgis/measure/keepbaseunit" ), true ).toBool();
684 
685  return QgsDistanceArea::formatArea( area, 3, displayAreaUnits(), baseUnit );
686 }
687 
689 {
690  QList<IdentifyResult> results;
691  if ( identifyRasterLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel ) )
692  {
693  emit changedRasterResults( results );
694  }
695 }
696 
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.
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:72
A rectangle specified with double values.
Definition: qgsrectangle.h:36
Base class for all map layer types.
Definition: qgsmaplayer.h:52
double y
Definition: qgspoint.h:148
Depends on scale if feature will be rendered (rule based )
Definition: qgsrenderer.h:193
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:202
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
void changedRasterResults(QList< IdentifyResult > &)
virtual void activate() override
called when set as currently active map tool
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:177
Use exact geometry intersection (slower) instead of bounding boxes.
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:518
void identifyProgress(int, int)
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
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:192
QList< QgsFeatureStore > QgsFeatureStoreList
virtual QString generateBandName(int theBandNumber) const
helper function to create zero padded band names
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:360
void setSourceCrs(long srsid)
sets source spatial reference system (by QGIS CRS)
bool hasCrsTransformEnabled()
A simple helper method to find out if on the fly projections are enabled or not.
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:41
static void circleCenterRadius(const QgsPointV2 &pt1, const QgsPointV2 &pt2, const QgsPointV2 &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
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.
QgsPoint toMapCoordinates(int x, int y) const
virtual QgsPointV2 startPoint() const =0
Returns the starting point of the curve.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
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 ellipsoid by its acronym.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
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 z() const
Returns the point&#39;s z-coordinate.
Definition: qgspointv2.h:82
void identifyMessage(const QString &)
double y() const
Returns the point&#39;s y-coordinate.
Definition: qgspointv2.h:76
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:199
virtual QgsPointV2 endPoint() const =0
Returns the end point of the curve.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:96
QgsMapToolIdentify(QgsMapCanvas *canvas)
constructor
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:715
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:72
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:38
QgsFields fields() const
Returns the list of fields of this layer.
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:82
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:192
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)=0
Needs to be called when a new render cycle is started.
QgsIdentifyMenu * mIdentifyMenu
QCursor mCursor
cursor used in map tool
Definition: qgsmaptool.h:195
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:65
virtual QgsRasterIdentifyResult identify(const QgsPoint &thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent=QgsRectangle(), int theWidth=0, int theHeight=0, int theDpi=96)
Identify raster value(s) found on the point position.
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
QgsPoint toLayerCoordinates(QgsMapLayer *layer, QPoint point)
transformation from screen coordinates to layer&#39;s coordinates
Definition: qgsmaptool.cpp:53
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< 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
Return the calculated scale of the map.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:212
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:36
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:182
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
static QgsPointV2 closestVertex(const QgsAbstractGeometry &geom, const QgsPointV2 &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
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.
double x() const
Returns the point&#39;s x-coordinate.
Definition: qgspointv2.h:70
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:33
virtual void deactivate()
called when map tool is being deactivated
Definition: qgsmaptool.cpp:98
Abstract base class for all geometries.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
A class to represent a point.
Definition: qgspoint.h:143
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
bool identifyRasterLayer(QList< IdentifyResult > *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle &viewExtent, double mapUnitsPerPixel)
QString toString() const
String representation of the point (x,y)
Definition: qgspoint.cpp:163
QMap< int, QVariant > results() const
Get results.
double measureArea(const QgsGeometry *geometry) const
Measures the area of a geometry.
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:131
Abstract base class for all map tools.
Definition: qgsmaptool.h:49
General purpose distance and area calculator.
double measurePerimeter(const QgsGeometry *geometry) const
Measures the perimeter of a polygon geometry.
QgsUnitTypes::DistanceUnit mapUnits() const
Get the current canvas map units.
Contains information about the context of a rendering operation.
virtual void stopRender(QgsRenderContext &context)=0
Needs to be called when a render cycle has finished to clean up.
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer&#39;s CRS to output CRS
virtual 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:187
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:355
virtual QgsPointV2 vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
QMap< QString, QVariant > params() const
Get map of optional parameters.
bool identifyLayer(QList< IdentifyResult > *results, QgsMapLayer *layer, const QgsPoint &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType=AllLayers)
Call the right method depending on layer type.
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:33
double measureLength(const QgsGeometry *geometry) const
Measures the length of a geometry.
QgsFields & fields()
Get fields list.
QString name
Read property of QString layerName.
Definition: qgsmaplayer.h:56
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:762
QgsRasterDataProvider * dataProvider()
Returns the data provider.
Custom exception class for Coordinate Reference System related exceptions.
virtual Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:210
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
Definition: qgsproject.cpp:437
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)
long srsid() const
Returns the internal CRS ID, if available.
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:397
QString message(QgsErrorMessage::Format theFormat=QgsErrorMessage::Html) const
Full error messages description.
Definition: qgserror.cpp:50
bool identifyVectorLayer(QList< IdentifyResult > *results, QgsVectorLayer *layer, const QgsPoint &point)
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:65
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double m() const
Returns the point&#39;s m value.
Definition: qgspointv2.h:88
QgsFeatureList & features()
Get features list reference.
QgsMapLayer * layer(int index)
return the map layer at position index in the layer stack
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:172
void setEllipsoidalMode(bool flag)
Sets whether coordinates must be projected to ellipsoid before measuring.
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:217
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:147