QGIS/src/app/qgsmaptoolidentify.cpp
timlinux 8f60bc5095 More api cleanups mainly affecting gui
git-svn-id: http://svn.osgeo.org/qgis/trunk@9233 c8812cc2-4d05-0410-92ff-de0c093fc19c
2008-08-31 18:28:22 +00:00

502 lines
14 KiB
C++

/***************************************************************************
qgsmaptoolidentify.cpp - map tool for identifying features
---------------------
begin : January 2006
copyright : (C) 2006 by Martin Dobias
email : wonder.sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/* $Id$ */
#include "qgscursors.h"
#include "qgsdistancearea.h"
#include "qgsfeature.h"
#include "qgsfield.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
#include "qgsidentifyresults.h"
#include "qgsmapcanvas.h"
#include "qgsmaptopixel.h"
#include "qgsmessageviewer.h"
#include "qgsmaptoolidentify.h"
#include "qgsrasterlayer.h"
#include "qgsrubberband.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgsattributedialog.h"
#include <QSettings>
#include <QMessageBox>
#include <QMouseEvent>
#include <QCursor>
#include <QPixmap>
#include "qgslogger.h"
QgsMapToolIdentify::QgsMapToolIdentify( QgsMapCanvas* canvas )
: QgsMapTool( canvas ),
mResults( 0 ),
mRubberBand( 0 )
{
// set cursor
QPixmap myIdentifyQPixmap = QPixmap(( const char ** ) identify_cursor );
mCursor = QCursor( myIdentifyQPixmap, 1, 1 );
}
QgsMapToolIdentify::~QgsMapToolIdentify()
{
if ( mResults )
{
mResults->done( 0 );
}
delete mRubberBand;
}
void QgsMapToolIdentify::canvasMoveEvent( QMouseEvent * e )
{
}
void QgsMapToolIdentify::canvasPressEvent( QMouseEvent * e )
{
}
void QgsMapToolIdentify::canvasReleaseEvent( QMouseEvent * e )
{
if ( !mCanvas || mCanvas->isDrawing() )
{
return;
}
mLayer = mCanvas->currentLayer();
// delete rubber band if there was any
delete mRubberBand;
mRubberBand = 0;
// call identify method for selected layer
if ( mLayer )
{
// In the special case of the WMS provider,
// coordinates are sent back to the server as pixel coordinates
// not the layer's native CRS. So identify on screen coordinates!
if (
( mLayer->type() == QgsMapLayer::RASTER )
&&
( dynamic_cast<QgsRasterLayer*>( mLayer )->providerKey() == "wms" )
)
{
identifyRasterWmsLayer( QgsPoint( e->x(), e->y() ) );
}
else
{
// convert screen coordinates to map coordinates
QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( e->x(), e->y() );
if ( mLayer->type() == QgsMapLayer::VECTOR )
{
identifyVectorLayer( idPoint );
}
else if ( mLayer->type() == QgsMapLayer::RASTER )
{
identifyRasterLayer( idPoint );
}
else
{
QgsDebugMsg( "unknown layer type!" );
}
}
}
else
{
QMessageBox::warning( mCanvas,
QObject::tr( "No active layer" ),
QObject::tr( "To identify features, you must choose an active layer by clicking on its name in the legend" ) );
}
}
void QgsMapToolIdentify::identifyRasterLayer( const QgsPoint& point )
{
QgsRasterLayer *layer = dynamic_cast<QgsRasterLayer*>( mLayer );
if ( !layer )
return;
QMap<QString, QString> attributes;
layer->identify( point, attributes );
if ( !mResults )
{
QgsAttributeAction aa;
mResults = new QgsIdentifyResults( aa, mCanvas->window() );
mResults->setAttribute( Qt::WA_DeleteOnClose );
// Be informed when the dialog box is closed so that we can stop using it.
connect( mResults, SIGNAL( accepted() ), this, SLOT( resultsDialogGone() ) );
connect( mResults, SIGNAL( rejected() ), this, SLOT( resultsDialogGone() ) );
mResults->restorePosition();
}
else
{
mResults->clear();
}
mResults->setTitle( layer->name() );
mResults->setColumnText( 0, QObject::tr( "Band" ) );
QMap<QString, QString>::iterator it;
for ( it = attributes.begin(); it != attributes.end(); it++ )
{
mResults->addAttribute( it.key(), it.value() );
}
mResults->addAttribute( tr( "(clicked coordinate)" ), point.toString() );
mResults->showAllAttributes();
mResults->show();
}
void QgsMapToolIdentify::identifyRasterWmsLayer( const QgsPoint& point )
{
QgsRasterLayer *layer = dynamic_cast<QgsRasterLayer*>( mLayer );
if ( !layer )
{
return;
}
//if WMS layer does not cover the view origin,
//we need to map the view pixel coordinates
//to WMS layer pixel coordinates
QgsRect viewExtent = mCanvas->extent();
double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
if ( mapUnitsPerPixel == 0 )
{
return;
}
double xMinView = viewExtent.xMin();
double yMaxView = viewExtent.yMax();
QgsRect layerExtent = layer->extent();
double xMinLayer = layerExtent.xMin();
double yMaxLayer = layerExtent.yMax();
double i, j;
if ( xMinView < xMinLayer )
{
i = ( int )( point.x() - ( xMinLayer - xMinView ) / mapUnitsPerPixel );
}
else
{
i = point.x();
}
if ( yMaxView > yMaxLayer )
{
j = ( int )( point.y() - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel );
}
else
{
j = point.y();
}
QString text = layer->identifyAsText( QgsPoint( i, j ) );
if ( text.isEmpty() )
{
showError();
return;
}
QgsMessageViewer* viewer = new QgsMessageViewer();
viewer->setWindowTitle( layer->name() );
viewer->setMessageAsPlainText( QString( tr( "WMS identify result for %1\n%2" ) ).arg( point.toString() ).arg( text ) );
viewer->showMessage(); // deletes itself on close
}
void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point )
{
QgsVectorLayer *layer = dynamic_cast<QgsVectorLayer*>( mLayer );
if ( !layer )
return;
// load identify radius from settings
QSettings settings;
double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble();
QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString();
// create the search rectangle
double searchRadius = mCanvas->extent().width() * ( identifyValue / 100.0 );
QgsRect r;
r.setXMinimum( point.x() - searchRadius );
r.setXMaximum( point.x() + searchRadius );
r.setYMinimum( point.y() - searchRadius );
r.setYMaximum( point.y() + searchRadius );
r = toLayerCoordinates( layer, r );
int featureCount = 0;
//QgsFeature feat;
QgsAttributeAction& actions = *layer->actions();
QString fieldIndex = layer->displayField();
const QgsFieldMap& fields = layer->pendingFields();
// init distance/area calculator
QgsDistanceArea calc;
calc.setProjectionsEnabled( mCanvas->projectionsEnabled() ); // project?
calc.setEllipsoid( ellipsoid );
calc.setSourceCRS( layer->srs().srsid() );
mFeatureList.clear();
QApplication::setOverrideCursor( Qt::WaitCursor );
layer->select( layer->pendingAllAttributesList(), r, true, true );
QgsFeature f;
while ( layer->getNextFeature( f ) )
mFeatureList << QgsFeature( f );
QApplication::restoreOverrideCursor();
if ( layer->isEditable() && mFeatureList.size() == 1 )
{
editFeature( mFeatureList[0] );
return;
}
// display features falling within the search radius
if ( !mResults )
{
mResults = new QgsIdentifyResults( actions, mCanvas->window() );
mResults->setAttribute( Qt::WA_DeleteOnClose );
// Be informed when the dialog box is closed so that we can stop using it.
connect( mResults, SIGNAL( accepted() ), this, SLOT( resultsDialogGone() ) );
connect( mResults, SIGNAL( rejected() ), this, SLOT( resultsDialogGone() ) );
connect( mResults, SIGNAL( selectedFeatureChanged( int ) ), this, SLOT( highlightFeature( int ) ) );
connect( mResults, SIGNAL( editFeature( int ) ), this, SLOT( editFeature( int ) ) );
// restore the identify window position and show it
mResults->restorePosition();
}
else
{
mResults->raise();
mResults->clear();
mResults->setActions( actions );
}
QApplication::setOverrideCursor( Qt::WaitCursor );
int lastFeatureId = 0;
QgsFeatureList::iterator f_it = mFeatureList.begin();
for ( ; f_it != mFeatureList.end(); ++f_it )
{
featureCount++;
QTreeWidgetItem* featureNode = mResults->addNode( "foo" );
featureNode->setData( 0, Qt::UserRole, QVariant( f_it->featureId() ) ); // save feature id
lastFeatureId = f_it->featureId();
featureNode->setText( 0, fieldIndex );
if ( layer->isEditable() )
mResults->addEdit( featureNode, f_it->featureId() );
const QgsAttributeMap& attr = f_it->attributeMap();
for ( QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it )
{
//QgsDebugMsg(it->fieldName() + " == " + fieldIndex);
if ( fields[it.key()].name() == fieldIndex )
{
featureNode->setText( 1, it->toString() );
}
mResults->addAttribute( featureNode, fields[it.key()].name(), it->isNull() ? "NULL" : it->toString() );
}
// Calculate derived attributes and insert:
// measure distance or area depending on geometry type
if ( layer->vectorType() == QGis::Line )
{
double dist = calc.measure( f_it->geometry() );
QString str = calc.textUnit( dist, 3, mCanvas->mapUnits(), false );
mResults->addDerivedAttribute( featureNode, QObject::tr( "Length" ), str );
if ( f_it->geometry()->wkbType() == QGis::WKBLineString )
{
// Add the start and end points in as derived attributes
str.setNum( f_it->geometry()->asPolyline().first().x(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "startX", str );
str.setNum( f_it->geometry()->asPolyline().first().y(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "startY", str );
str.setNum( f_it->geometry()->asPolyline().last().x(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "endX", str );
str.setNum( f_it->geometry()->asPolyline().last().y(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "endY", str );
}
}
else if ( layer->vectorType() == QGis::Polygon )
{
double area = calc.measure( f_it->geometry() );
QString str = calc.textUnit( area, 3, mCanvas->mapUnits(), true );
mResults->addDerivedAttribute( featureNode, QObject::tr( "Area" ), str );
}
else if ( layer->vectorType() == QGis::Point )
{
// Include the x and y coordinates of the point as a derived attribute
QString str;
str.setNum( f_it->geometry()->asPoint().x(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "X", str );
str.setNum( f_it->geometry()->asPoint().y(), 'g', 10 );
mResults->addDerivedAttribute( featureNode, "Y", str );
}
// Add actions
QgsAttributeAction::aIter iter = actions.begin();
for ( register int i = 0; iter != actions.end(); ++iter, ++i )
{
mResults->addAction( featureNode, i, QObject::tr( "action" ), iter->name() );
}
}
QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) );
//also test the not commited features //todo: eliminate copy past code
mResults->setTitle( layer->name() + " - " + QString::number( featureCount ) + QObject::tr( " features found" ) );
if ( featureCount == 1 )
{
mResults->showAllAttributes();
mResults->setTitle( layer->name() + " - " + QObject::tr( " 1 feature found" ) );
highlightFeature( lastFeatureId );
}
else if ( featureCount == 0 )
{
mResults->setTitle( layer->name() + " - " + QObject::tr( "No features found" ) );
mResults->setMessage( QObject::tr( "No features found" ), QObject::tr( "No features were found in the active layer at the point you clicked" ) );
}
else
{
QString title = layer->name();
title += QString( tr( "- %1 features found", "Identify results window title", featureCount ) ).arg( featureCount );
mResults->setTitle( title );
}
QApplication::restoreOverrideCursor();
mResults->show();
}
void QgsMapToolIdentify::showError()
{
// QMessageBox::warning(
// this,
// mapLayer->errorCaptionString(),
// tr("Could not draw") + " " + mapLayer->name() + " " + tr("because") + ":\n" +
// mapLayer->errorString()
// );
QgsMessageViewer * mv = new QgsMessageViewer();
mv->setWindowTitle( mLayer->errorCaptionString() );
mv->setMessageAsPlainText(
QObject::tr( "Could not identify objects on" ) + " " + mLayer->name() + " " + QObject::tr( "because" ) + ":\n" +
mLayer->errorString()
);
mv->exec(); // deletes itself on close
}
void QgsMapToolIdentify::resultsDialogGone()
{
mResults = 0;
delete mRubberBand;
mRubberBand = 0;
}
void QgsMapToolIdentify::deactivate()
{
if ( mResults )
mResults->done( 0 ); // close the window
QgsMapTool::deactivate();
}
void QgsMapToolIdentify::highlightFeature( int featureId )
{
QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( mLayer );
if ( !layer )
return;
delete mRubberBand;
mRubberBand = 0;
QgsFeature feat;
if ( layer->getFeatureAtId( featureId, feat, true, false ) != 0 )
{
return;
}
if ( !feat.geometry() )
{
return;
}
mRubberBand = new QgsRubberBand( mCanvas, feat.geometry()->vectorType() == QGis::Polygon );
if ( mRubberBand )
{
mRubberBand->setToGeometry( feat.geometry(), *layer );
mRubberBand->setWidth( 2 );
mRubberBand->setColor( Qt::red );
mRubberBand->show();
}
}
void QgsMapToolIdentify::editFeature( int featureId )
{
for ( QgsFeatureList::iterator it = mFeatureList.begin(); it != mFeatureList.end(); it++ )
{
if ( it->featureId() == featureId )
{
editFeature( *it );
break;
}
}
}
void QgsMapToolIdentify::editFeature( QgsFeature &f )
{
QgsVectorLayer* layer = dynamic_cast<QgsVectorLayer*>( mLayer );
if ( !layer )
return;
if ( !layer->isEditable() )
return;
QgsAttributeMap src = f.attributeMap();
QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f );
if ( ad->exec() )
{
const QgsAttributeMap &dst = f.attributeMap();
for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ )
{
if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
layer->changeAttributeValue( f.featureId(), it.key(), it.value() );
}
}
delete ad;
mCanvas->refresh();
}