~ubuntu-branches/ubuntu/wily/qgis/wily

« back to all changes in this revision

Viewing changes to src/app/qgsmaptoolidentify.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Johan Van de Wauw
  • Date: 2010-07-11 20:23:24 UTC
  • mfrom: (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100711202324-5ktghxa7hracohmr
Tags: 1.4.0+12730-3ubuntu1
* Merge from Debian unstable (LP: #540941).
* Fix compilation issues with QT 4.7
* Add build-depends on libqt4-webkit-dev 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/* $Id$ */
 
16
 
 
17
#include "qgscursors.h"
 
18
#include "qgsdistancearea.h"
 
19
#include "qgsfeature.h"
 
20
#include "qgsfield.h"
 
21
#include "qgsgeometry.h"
 
22
#include "qgslogger.h"
 
23
#include "qgsidentifyresults.h"
 
24
#include "qgsmapcanvas.h"
 
25
#include "qgsmaptopixel.h"
 
26
#include "qgsmessageviewer.h"
 
27
#include "qgsmaptoolidentify.h"
 
28
#include "qgsrasterlayer.h"
 
29
#include "qgscoordinatereferencesystem.h"
 
30
#include "qgsvectordataprovider.h"
 
31
#include "qgsvectorlayer.h"
 
32
#include "qgsproject.h"
 
33
#include "qgsmaplayerregistry.h"
 
34
#include "qgisapp.h"
 
35
 
 
36
#include <QSettings>
 
37
#include <QMessageBox>
 
38
#include <QMouseEvent>
 
39
#include <QCursor>
 
40
#include <QPixmap>
 
41
#include <QStatusBar>
 
42
 
 
43
QgsMapToolIdentify::QgsMapToolIdentify( QgsMapCanvas* canvas )
 
44
    : QgsMapTool( canvas )
 
45
{
 
46
  // set cursor
 
47
  QPixmap myIdentifyQPixmap = QPixmap(( const char ** ) identify_cursor );
 
48
  mCursor = QCursor( myIdentifyQPixmap, 1, 1 );
 
49
}
 
50
 
 
51
QgsMapToolIdentify::~QgsMapToolIdentify()
 
52
{
 
53
  if ( mResults )
 
54
  {
 
55
    mResults->done( 0 );
 
56
  }
 
57
}
 
58
 
 
59
QgsIdentifyResults *QgsMapToolIdentify::results()
 
60
{
 
61
  if ( !mResults )
 
62
    mResults = new QgsIdentifyResults( mCanvas, mCanvas->window() );
 
63
 
 
64
  return mResults;
 
65
}
 
66
 
 
67
void QgsMapToolIdentify::canvasMoveEvent( QMouseEvent * e )
 
68
{
 
69
}
 
70
 
 
71
void QgsMapToolIdentify::canvasPressEvent( QMouseEvent * e )
 
72
{
 
73
}
 
74
 
 
75
void QgsMapToolIdentify::canvasReleaseEvent( QMouseEvent * e )
 
76
{
 
77
  if ( !mCanvas || mCanvas->isDrawing() )
 
78
  {
 
79
    return;
 
80
  }
 
81
 
 
82
  results()->clear();
 
83
 
 
84
  QSettings settings;
 
85
  int identifyMode = settings.value( "/Map/identifyMode", 0 ).toInt();
 
86
 
 
87
  bool res = false;
 
88
 
 
89
  if ( identifyMode == 0 )
 
90
  {
 
91
    QgsMapLayer *layer = mCanvas->currentLayer();
 
92
 
 
93
    if ( !layer )
 
94
    {
 
95
      QMessageBox::warning( mCanvas,
 
96
                            tr( "No active layer" ),
 
97
                            tr( "To identify features, you must choose an active layer by clicking on its name in the legend" ) );
 
98
      return;
 
99
    }
 
100
 
 
101
    QApplication::setOverrideCursor( Qt::WaitCursor );
 
102
 
 
103
    res = identifyLayer( layer, e->x(), e->y() );
 
104
 
 
105
    QApplication::restoreOverrideCursor();
 
106
  }
 
107
  else
 
108
  {
 
109
    connect( this, SIGNAL( identifyProgress( int, int ) ), QgisApp::instance(), SLOT( showProgress( int, int ) ) );
 
110
    connect( this, SIGNAL( identifyMessage( QString ) ), QgisApp::instance(), SLOT( showStatusMessage( QString ) ) );
 
111
 
 
112
    QApplication::setOverrideCursor( Qt::WaitCursor );
 
113
 
 
114
    QStringList noIdentifyLayerIdList = QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
 
115
 
 
116
    for ( int i = 0; i < mCanvas->layerCount(); i++ )
 
117
    {
 
118
      QgsMapLayer *layer = mCanvas->layer( i );
 
119
 
 
120
      emit identifyProgress( i, mCanvas->layerCount() );
 
121
      emit identifyMessage( tr( "Identifying on %1..." ).arg( layer->name() ) );
 
122
 
 
123
      if ( noIdentifyLayerIdList.contains( layer->getLayerID() ) )
 
124
        continue;
 
125
 
 
126
      if ( identifyLayer( layer, e->x(), e->y() ) )
 
127
      {
 
128
        res = true;
 
129
        if ( identifyMode == 1 )
 
130
          break;
 
131
      }
 
132
    }
 
133
 
 
134
    emit identifyProgress( mCanvas->layerCount(), mCanvas->layerCount() );
 
135
    emit identifyMessage( tr( "Identifying done." ) );
 
136
 
 
137
    disconnect( this, SIGNAL( identifyProgress( int, int ) ), QgisApp::instance(), SLOT( showProgress( int, int ) ) );
 
138
    disconnect( this, SIGNAL( identifyMessage( QString ) ), QgisApp::instance(), SLOT( showStatusMessage( QString ) ) );
 
139
 
 
140
    QApplication::restoreOverrideCursor();
 
141
  }
 
142
 
 
143
  if ( res )
 
144
  {
 
145
    results()->show();
 
146
    results()->raise();
 
147
  }
 
148
  else
 
149
  {
 
150
    QSettings mySettings;
 
151
    bool myDockFlag = mySettings.value( "/qgis/dockIdentifyResults", false ).toBool();
 
152
    if ( !myDockFlag )
 
153
    {
 
154
      results()->hide();
 
155
    }
 
156
    else
 
157
    {
 
158
      results()->clear();
 
159
    }
 
160
    QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) );
 
161
  }
 
162
}
 
163
 
 
164
void QgsMapToolIdentify::activate()
 
165
{
 
166
  results()->activate();
 
167
  QgsMapTool::activate();
 
168
}
 
169
 
 
170
void QgsMapToolIdentify::deactivate()
 
171
{
 
172
  results()->deactivate();
 
173
  QgsMapTool::deactivate();
 
174
}
 
175
 
 
176
bool QgsMapToolIdentify::identifyLayer( QgsMapLayer *layer, int x, int y )
 
177
{
 
178
  bool res = false;
 
179
 
 
180
  if ( layer->type() == QgsMapLayer::RasterLayer )
 
181
  {
 
182
    res = identifyRasterLayer( qobject_cast<QgsRasterLayer *>( layer ), x, y );
 
183
  }
 
184
  else
 
185
  {
 
186
    res = identifyVectorLayer( qobject_cast<QgsVectorLayer *>( layer ), x, y );
 
187
  }
 
188
 
 
189
  return res;
 
190
}
 
191
 
 
192
 
 
193
bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int y )
 
194
{
 
195
  if ( !layer )
 
196
    return false;
 
197
 
 
198
  QMap< QString, QString > attributes, derivedAttributes;
 
199
 
 
200
  QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );
 
201
 
 
202
  derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() );
 
203
 
 
204
  // load identify radius from settings
 
205
  QSettings settings;
 
206
  double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble();
 
207
  QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString();
 
208
 
 
209
  if ( identifyValue <= 0.0 )
 
210
    identifyValue = QGis::DEFAULT_IDENTIFY_RADIUS;
 
211
 
 
212
  int featureCount = 0;
 
213
  const QgsFieldMap& fields = layer->pendingFields();
 
214
 
 
215
  // init distance/area calculator
 
216
  QgsDistanceArea calc;
 
217
  calc.setProjectionsEnabled( mCanvas->hasCrsTransformEnabled() ); // project?
 
218
  calc.setEllipsoid( ellipsoid );
 
219
  calc.setSourceCrs( layer->srs().srsid() );
 
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 = mCanvas->extent().width() * ( identifyValue / 100.0 );
 
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
    layer->select( layer->pendingAllAttributesList(), r, true, true );
 
240
    QgsFeature f;
 
241
    while ( layer->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
  for ( ; f_it != featureList.end(); ++f_it )
 
254
  {
 
255
    featureCount++;
 
256
 
 
257
    int fid = f_it->id();
 
258
    QString displayField, displayValue;
 
259
    QMap<QString, QString> attributes, derivedAttributes;
 
260
 
 
261
    const QgsAttributeMap& attr = f_it->attributeMap();
 
262
 
 
263
    for ( QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it )
 
264
    {
 
265
      QString attributeName  = layer->attributeDisplayName( it.key() );
 
266
      QString attributeValue = it->isNull() ? "NULL" : it->toString();
 
267
 
 
268
      switch ( layer->editType( it.key() ) )
 
269
      {
 
270
        case QgsVectorLayer::Hidden:
 
271
          continue;
 
272
 
 
273
        case QgsVectorLayer::ValueMap:
 
274
          attributeValue = layer->valueMap( it.key() ).key( it->toString(), QString( "(%1)" ).arg( it->toString() ) );
 
275
          break;
 
276
 
 
277
        default:
 
278
          break;
 
279
      }
 
280
 
 
281
      if ( fields[it.key()].name() == layer->displayField() )
 
282
      {
 
283
        displayField = attributeName;
 
284
        displayValue = attributeValue;
 
285
      }
 
286
 
 
287
      attributes.insert( attributeName, attributeValue );
 
288
    }
 
289
 
 
290
    // Calculate derived attributes and insert:
 
291
    // measure distance or area depending on geometry type
 
292
    if ( layer->geometryType() == QGis::Line )
 
293
    {
 
294
      double dist = calc.measure( f_it->geometry() );
 
295
      QGis::UnitType myDisplayUnits;
 
296
      convertMeasurement( calc, dist, myDisplayUnits, false );
 
297
      QString str = calc.textUnit( dist, 3, myDisplayUnits, false );  // dist and myDisplayUnits are out params
 
298
      derivedAttributes.insert( tr( "Length" ), str );
 
299
      if ( f_it->geometry()->wkbType() == QGis::WKBLineString )
 
300
      {
 
301
        // Add the start and end points in as derived attributes
 
302
        str = QLocale::system().toString( f_it->geometry()->asPolyline().first().x(), 'g', 10 );
 
303
        derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str );
 
304
        str = QLocale::system().toString( f_it->geometry()->asPolyline().first().y(), 'g', 10 );
 
305
        derivedAttributes.insert( tr( "firstY" ), str );
 
306
        str = QLocale::system().toString( f_it->geometry()->asPolyline().last().x(), 'g', 10 );
 
307
        derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str );
 
308
        str = QLocale::system().toString( f_it->geometry()->asPolyline().last().y(), 'g', 10 );
 
309
        derivedAttributes.insert( tr( "lastY" ), str );
 
310
      }
 
311
    }
 
312
    else if ( layer->geometryType() == QGis::Polygon )
 
313
    {
 
314
      double area = calc.measure( f_it->geometry() );
 
315
      QGis::UnitType myDisplayUnits;
 
316
      convertMeasurement( calc, area, myDisplayUnits, true );  // area and myDisplayUnits are out params
 
317
      QString str = calc.textUnit( area, 3, myDisplayUnits, true );
 
318
      derivedAttributes.insert( tr( "Area" ), str );
 
319
    }
 
320
    else if ( layer->geometryType() == QGis::Point )
 
321
    {
 
322
      // Include the x and y coordinates of the point as a derived attribute
 
323
      QString str;
 
324
      str = QLocale::system().toString( f_it->geometry()->asPoint().x(), 'g', 10 );
 
325
      derivedAttributes.insert( "X", str );
 
326
      str = QLocale::system().toString( f_it->geometry()->asPoint().y(), 'g', 10 );
 
327
      derivedAttributes.insert( "Y", str );
 
328
    }
 
329
 
 
330
    derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : QString::number( fid ) );
 
331
 
 
332
    results()->addFeature( layer, fid, displayField, displayValue, attributes, derivedAttributes );
 
333
  }
 
334
 
 
335
  QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) );
 
336
 
 
337
  return featureCount > 0;
 
338
}
 
339
 
 
340
bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y )
 
341
{
 
342
  bool res = true;
 
343
 
 
344
  if ( !layer )
 
345
    return false;
 
346
 
 
347
  QgsRasterDataProvider *dprovider = layer->dataProvider();
 
348
  if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 )
 
349
    return false;
 
350
 
 
351
  QMap< QString, QString > attributes, derivedAttributes;
 
352
  QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );
 
353
  QString type;
 
354
 
 
355
  if ( layer->providerKey() == "wms" )
 
356
  {
 
357
    type = tr( "WMS layer" );
 
358
 
 
359
    //if WMS layer does not cover the view origin,
 
360
    //we need to map the view pixel coordinates
 
361
    //to WMS layer pixel coordinates
 
362
    QgsRectangle viewExtent = mCanvas->extent();
 
363
    QgsRectangle layerExtent = layer->extent();
 
364
    double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
 
365
    if ( mapUnitsPerPixel > 0 && viewExtent.intersects( layerExtent ) )
 
366
    {
 
367
      double xMinView = viewExtent.xMinimum();
 
368
      double yMaxView = viewExtent.yMaximum();
 
369
      double xMinLayer = layerExtent.xMinimum();
 
370
      double yMaxLayer = layerExtent.yMaximum();
 
371
 
 
372
      idPoint.set(
 
373
        xMinView < xMinLayer ? floor( x - ( xMinLayer - xMinView ) / mapUnitsPerPixel ) : x,
 
374
        yMaxView > yMaxLayer ? floor( y - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ) : y
 
375
      );
 
376
 
 
377
      attributes.insert( tr( "Feature info" ), layer->identifyAsText( idPoint ) );
 
378
    }
 
379
    else
 
380
    {
 
381
      res = false;
 
382
    }
 
383
  }
 
384
  else
 
385
  {
 
386
    type = tr( "Raster" );
 
387
    res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes );
 
388
  }
 
389
 
 
390
  if ( res )
 
391
  {
 
392
    derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() );
 
393
    results()->addFeature( layer, -1, type, "", attributes, derivedAttributes );
 
394
  }
 
395
 
 
396
  return res;
 
397
}
 
398
 
 
399
 
 
400
void QgsMapToolIdentify::convertMeasurement( QgsDistanceArea &calc, double &measure, QGis::UnitType &u, bool isArea )
 
401
{
 
402
  // Helper for converting between meters and feet
 
403
  // The parameter &u is out only...
 
404
 
 
405
  QGis::UnitType myUnits = mCanvas->mapUnits();
 
406
  if (( myUnits == QGis::Degrees || myUnits == QGis::Feet ) &&
 
407
      calc.ellipsoid() != "NONE" &&
 
408
      calc.hasCrsTransformEnabled() )
 
409
  {
 
410
    // Measuring on an ellipsoid returns meters, and so does using projections???
 
411
    myUnits = QGis::Meters;
 
412
    QgsDebugMsg( "We're measuring on an ellipsoid or using projections, the system is returning meters" );
 
413
  }
 
414
 
 
415
  // Get the units for display
 
416
  QSettings settings;
 
417
  QString myDisplayUnitsTxt = settings.value( "/qgis/measure/displayunits", "meters" ).toString();
 
418
 
 
419
  // Only convert between meters and feet
 
420
  if ( myUnits == QGis::Meters && myDisplayUnitsTxt == "feet" )
 
421
  {
 
422
    QgsDebugMsg( QString( "Converting %1 meters" ).arg( QString::number( measure ) ) );
 
423
    measure /= 0.3048;
 
424
    if ( isArea )
 
425
    {
 
426
      measure /= 0.3048;
 
427
    }
 
428
    QgsDebugMsg( QString( "to %1 feet" ).arg( QString::number( measure ) ) );
 
429
    myUnits = QGis::Feet;
 
430
  }
 
431
  if ( myUnits == QGis::Feet && myDisplayUnitsTxt == "meters" )
 
432
  {
 
433
    QgsDebugMsg( QString( "Converting %1 feet" ).arg( QString::number( measure ) ) );
 
434
    measure *= 0.3048;
 
435
    if ( isArea )
 
436
    {
 
437
      measure *= 0.3048;
 
438
    }
 
439
    QgsDebugMsg( QString( "to %1 meters" ).arg( QString::number( measure ) ) );
 
440
    myUnits = QGis::Meters;
 
441
  }
 
442
 
 
443
  u = myUnits;
 
444
}