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