QGIS API Documentation  2.99.0-Master (cb63e82)
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 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.setEllipsoid( ellipsoid );
357  calc.setSourceCrs( layer->crs() );
358 
361 
362  QgsVertexId vId;
363  QgsPointV2 closestPoint;
364  if ( feature->hasGeometry() )
365  {
366  geometryType = feature->geometry().type();
367  wkbType = feature->geometry().geometry()->wkbType();
368  //find closest vertex to clicked point
369  closestPoint = QgsGeometryUtils::closestVertex( *feature->geometry().geometry(), QgsPointV2( layerPoint.x(), layerPoint.y() ), vId );
370  }
371 
372  if ( QgsWkbTypes::isMultiType( wkbType ) )
373  {
374  QString str = QLocale::system().toString( static_cast<const QgsGeometryCollection *>( feature->geometry().geometry() )->numGeometries() );
375  derivedAttributes.insert( tr( "Parts" ), str );
376  str = QLocale::system().toString( vId.part + 1 );
377  derivedAttributes.insert( tr( "Part number" ), str );
378  }
379 
380  if ( geometryType == QgsWkbTypes::LineGeometry )
381  {
382  double dist = calc.measureLength( feature->geometry() );
383  dist = calc.convertLengthMeasurement( dist, displayDistanceUnits() );
384  QString str = formatDistance( dist );
385  derivedAttributes.insert( tr( "Length" ), str );
386 
387  const QgsCurve *curve = dynamic_cast< const QgsCurve * >( feature->geometry().geometry() );
388  if ( curve )
389  {
390  str = QLocale::system().toString( curve->nCoordinates() );
391  derivedAttributes.insert( tr( "Vertices" ), str );
392 
393  //add details of closest vertex to identify point
394  closestVertexAttributes( *curve, vId, layer, derivedAttributes );
395 
396  // Add the start and end points in as derived attributes
397  QgsPoint pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( curve->startPoint().x(), curve->startPoint().y() ) );
398  str = formatXCoordinate( pnt );
399  derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
400  str = formatYCoordinate( pnt );
401  derivedAttributes.insert( tr( "firstY" ), str );
402  pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( curve->endPoint().x(), curve->endPoint().y() ) );
403  str = formatXCoordinate( pnt );
404  derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
405  str = formatYCoordinate( pnt );
406  derivedAttributes.insert( tr( "lastY" ), str );
407  }
408  }
409  else if ( geometryType == QgsWkbTypes::PolygonGeometry )
410  {
411  double area = calc.measureArea( feature->geometry() );
412  area = calc.convertAreaMeasurement( area, displayAreaUnits() );
413  QString str = formatArea( area );
414  derivedAttributes.insert( tr( "Area" ), str );
415 
416  double perimeter = calc.measurePerimeter( feature->geometry() );
417  perimeter = calc.convertLengthMeasurement( perimeter, displayDistanceUnits() );
418  str = formatDistance( perimeter );
419  derivedAttributes.insert( tr( "Perimeter" ), str );
420 
421  str = QLocale::system().toString( feature->geometry().geometry()->nCoordinates() );
422  derivedAttributes.insert( tr( "Vertices" ), str );
423 
424  //add details of closest vertex to identify point
425  closestVertexAttributes( *feature->geometry().geometry(), vId, layer, derivedAttributes );
426  }
427  else if ( geometryType == QgsWkbTypes::PointGeometry &&
429  {
430  // Include the x and y coordinates of the point as a derived attribute
431  QgsPoint pnt = mCanvas->mapSettings().layerToMapCoordinates( layer, feature->geometry().asPoint() );
432  QString str = formatXCoordinate( pnt );
433  derivedAttributes.insert( QStringLiteral( "X" ), str );
434  str = formatYCoordinate( pnt );
435  derivedAttributes.insert( QStringLiteral( "Y" ), str );
436 
437  if ( QgsWkbTypes::hasZ( wkbType ) )
438  {
439  str = QLocale::system().toString( static_cast<const QgsPointV2 *>( feature->geometry().geometry() )->z(), 'g', 10 );
440  derivedAttributes.insert( QStringLiteral( "Z" ), str );
441  }
442  if ( QgsWkbTypes::hasM( wkbType ) )
443  {
444  str = QLocale::system().toString( static_cast<const QgsPointV2 *>( feature->geometry().geometry() )->m(), 'g', 10 );
445  derivedAttributes.insert( QStringLiteral( "M" ), str );
446  }
447  }
448 
449  return derivedAttributes;
450 }
451 
452 bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle &viewExtent, double mapUnitsPerPixel )
453 {
454  QgsDebugMsg( "point = " + point.toString() );
455  if ( !layer )
456  return false;
457 
458  QgsRasterDataProvider *dprovider = layer->dataProvider();
459  if ( !dprovider )
460  return false;
461 
462  int capabilities = dprovider->capabilities();
463  if ( !( capabilities & QgsRasterDataProvider::Identify ) )
464  return false;
465 
466  QgsPoint pointInCanvasCrs = point;
467  try
468  {
469  point = toLayerCoordinates( layer, point );
470  }
471  catch ( QgsCsException &cse )
472  {
473  Q_UNUSED( cse );
474  QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) );
475  return false;
476  }
477  QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) );
478 
479  if ( !layer->extent().contains( point ) )
480  return false;
481 
482  QMap< QString, QString > attributes, derivedAttributes;
483 
484  QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() );
485 
486  // check if the format is really supported otherwise use first supported format
487  if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) )
488  {
490  else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue;
491  else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml;
492  else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText;
493  else return false;
494  }
495 
496  QgsRasterIdentifyResult identifyResult;
497  // We can only use current map canvas context (extent, width, height) if layer is not reprojected,
498  if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() )
499  {
500  // To get some reasonable response for point/line WMS vector layers we must
501  // use a context with approximately a resolution in layer CRS units
502  // corresponding to current map canvas resolution (for examplei UMN Mapserver
503  // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel
504  // + TOLERANCE (layer param) for feature selection)
505  //
506  QgsRectangle r;
507  r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. );
508  r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. );
509  r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. );
510  r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. );
511  r = toLayerCoordinates( layer, r ); // will be a bit larger
512  // Mapserver (6.0.3, for example) does not work with 1x1 pixel box
513  // but that is fixed (the rect is enlarged) in the WMS provider
514  identifyResult = dprovider->identify( point, format, r, 1, 1 );
515  }
516  else
517  {
518  // It would be nice to use the same extent and size which was used for drawing,
519  // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw()
520  // is doing some tricks with extent and size to align raster to output which
521  // would be difficult to replicate here.
522  // Note: cutting the extent may result in slightly different x and y resolutions
523  // and thus shifted point calculated back in QGIS WMS (using average resolution)
524  //viewExtent = dprovider->extent().intersect( &viewExtent );
525 
526  // Width and height are calculated from not projected extent and we hope that
527  // are similar to source width and height used to reproject layer for drawing.
528  // TODO: may be very dangerous, because it may result in different resolutions
529  // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution.
530  int width = qRound( viewExtent.width() / mapUnitsPerPixel );
531  int height = qRound( viewExtent.height() / mapUnitsPerPixel );
532 
533  QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) );
534  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) );
535  QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) );
536 
537  identifyResult = dprovider->identify( point, format, viewExtent, width, height );
538  }
539 
540  derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) );
541  derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) );
542 
543  if ( identifyResult.isValid() )
544  {
545  QMap<int, QVariant> values = identifyResult.results();
546  QgsGeometry geometry;
547  if ( format == QgsRaster::IdentifyFormatValue )
548  {
549  Q_FOREACH ( int bandNo, values.keys() )
550  {
551  QString valueString;
552  if ( values.value( bandNo ).isNull() )
553  {
554  valueString = tr( "no data" );
555  }
556  else
557  {
558  QVariant value( values.value( bandNo ) );
559  // The cast is legit. Quoting QT doc :
560  // "Although this function is declared as returning QVariant::Type,
561  // the return value should be interpreted as QMetaType::Type"
562  if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float )
563  {
564  valueString = QgsRasterBlock::printValue( value.toFloat() );
565  }
566  else
567  {
568  valueString = QgsRasterBlock::printValue( value.toDouble() );
569  }
570  }
571  attributes.insert( dprovider->generateBandName( bandNo ), valueString );
572  }
573  QString label = layer->name();
574  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
575  }
576  else if ( format == QgsRaster::IdentifyFormatFeature )
577  {
578  Q_FOREACH ( int i, values.keys() )
579  {
580  QVariant value = values.value( i );
581  if ( value.type() == QVariant::Bool && !value.toBool() )
582  {
583  // sublayer not visible or not queryable
584  continue;
585  }
586 
587  if ( value.type() == QVariant::String )
588  {
589  // error
590  // TODO: better error reporting
591  QString label = layer->subLayers().value( i );
592  attributes.clear();
593  attributes.insert( tr( "Error" ), value.toString() );
594 
595  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
596  continue;
597  }
598 
599  // list of feature stores for a single sublayer
600  QgsFeatureStoreList featureStoreList = values.value( i ).value<QgsFeatureStoreList>();
601 
602  Q_FOREACH ( QgsFeatureStore featureStore, featureStoreList )
603  {
604  Q_FOREACH ( QgsFeature feature, featureStore.features() )
605  {
606  attributes.clear();
607  // WMS sublayer and feature type, a sublayer may contain multiple feature types.
608  // Sublayer name may be the same as layer name and feature type name
609  // may be the same as sublayer. We try to avoid duplicities in label.
610  QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString();
611  QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString();
612  // Strip UMN MapServer '_feature'
613  featureType.remove( QStringLiteral( "_feature" ) );
614  QStringList labels;
615  if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 )
616  {
617  labels << sublayer;
618  }
619  if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() )
620  {
621  labels << featureType;
622  }
623 
624  QMap< QString, QString > derAttributes = derivedAttributes;
625  derAttributes.unite( featureDerivedAttributes( &feature, layer ) );
626 
627  IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QStringLiteral( " / " ) ), featureStore.fields(), feature, derAttributes );
628 
629  identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) );
630  results->append( identifyResult );
631  }
632  }
633  }
634  }
635  else // text or html
636  {
637  QgsDebugMsg( QString( "%1 HTML or text values" ).arg( values.size() ) );
638  Q_FOREACH ( int bandNo, values.keys() )
639  {
640  QString value = values.value( bandNo ).toString();
641  attributes.clear();
642  attributes.insert( QLatin1String( "" ), value );
643 
644  QString label = layer->subLayers().value( bandNo );
645  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
646  }
647  }
648  }
649  else
650  {
651  attributes.clear();
652  QString value = identifyResult.error().message( QgsErrorMessage::Text );
653  attributes.insert( tr( "Error" ), value );
654  QString label = tr( "Identify error" );
655  results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) );
656  }
657 
658  return true;
659 }
660 
661 QgsUnitTypes::DistanceUnit QgsMapToolIdentify::displayDistanceUnits() const
662 {
663  return mCanvas->mapUnits();
664 }
665 
666 QgsUnitTypes::AreaUnit QgsMapToolIdentify::displayAreaUnits() const
667 {
669 }
670 
671 QString QgsMapToolIdentify::formatDistance( double distance ) const
672 {
673  QgsSettings settings;
674  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
675 
676  return QgsDistanceArea::formatDistance( distance, 3, displayDistanceUnits(), baseUnit );
677 }
678 
679 QString QgsMapToolIdentify::formatArea( double area ) const
680 {
681  QgsSettings settings;
682  bool baseUnit = settings.value( QStringLiteral( "qgis/measure/keepbaseunit" ), true ).toBool();
683 
684  return QgsDistanceArea::formatArea( area, 3, displayAreaUnits(), baseUnit );
685 }
686 
688 {
689  QList<IdentifyResult> results;
690  if ( identifyRasterLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel ) )
691  {
692  emit changedRasterResults( results );
693  }
694 }
695 
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:72
A rectangle specified with double values.
Definition: qgsrectangle.h:38
Base class for all map layer types.
Definition: qgsmaplayer.h:53
double y
Definition: qgspoint.h:42
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:204
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
virtual QgsRasterIdentifyResult identify(const QgsPoint &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.
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: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:549
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
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:522
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:52
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 the ellipsoid by its acronym.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
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
QString ellipsoid
Definition: qgsproject.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:190
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:754
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.
QgsPoint layerToMapCoordinates(const QgsMapLayer *layer, QgsPoint point) const
transform point coordinates from layer&#39;s CRS to output CRS
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:192
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)=0
Needs to be called when a new render cycle is started.
QgsIdentifyMenu * mIdentifyMenu
QgsRasterDataProvider * dataProvider() override
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
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
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
Returns the width of the rectangle.
Definition: qgsrectangle.h:118
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:75
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
QgsPoint toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
transformation from screen coordinates to layer&#39;s coordinates
Definition: qgsmaptool.cpp:55
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
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
A class to represent a point.
Definition: qgspoint.h:37
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:45
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
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:135
Abstract base class for all map tools.
Definition: qgsmaptool.h:49
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.
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
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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:360
virtual QgsPointV2 vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
QMap< QString, QVariant > params() const
Returns the 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:37
QString name
Definition: qgsmaplayer.h:57
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:803
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.
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 QString generateBandName(int bandNumber) const
helper function to create zero padded band names
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:422
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:66
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
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 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:41