Skip to content
Permalink
Browse files

[FEATURE] add "feature action" map tool to run actions directly when …

…clicking on features (fix #119 and #4262)

work done for Regione Toscana-SIGTA
  • Loading branch information
brushtyler committed Jan 11, 2012
1 parent 1fe82b3 commit acc1d558f52f2d36b13f74fed958ac44b6cbbf6e
@@ -50,6 +50,7 @@ SET(QGIS_APP_SRCS
qgsmaptooldeletepart.cpp
qgsmaptooldeletevertex.cpp
qgsmaptooledit.cpp
qgsmaptoolfeatureaction.cpp
qgsmaptoolformannotation.cpp
qgsmaptoolidentify.cpp
qgsmaptoollabel.cpp
@@ -189,6 +190,7 @@ SET (QGIS_APP_MOC_HDRS
qgsmaptooldeletepart.h
qgsmaptooldeletering.h
qgsmaptooldeletevertex.h
qgsmaptoolfeatureaction.h
qgsmaptoolidentify.h
qgsmaptoolmeasureangle.h
qgsmaptoolmovefeature.h
@@ -205,6 +205,7 @@
#include "qgsmaptooldeletering.h"
#include "qgsmaptooldeletepart.h"
#include "qgsmaptooldeletevertex.h"
#include "qgsmaptoolfeatureaction.h"
#include "qgsmaptoolformannotation.h"
#include "qgsmaptoolidentify.h"
#include "qgsmaptoolmeasureangle.h"
@@ -640,6 +641,7 @@ QgisApp::~QgisApp()
delete mMapTools.mZoomOut;
delete mMapTools.mPan;
delete mMapTools.mIdentify;
delete mMapTools.mFeatureAction;
delete mMapTools.mMeasureDist;
delete mMapTools.mMeasureArea;
delete mMapTools.mMeasureAngle;
@@ -814,6 +816,7 @@ void QgisApp::createActions()
connect( mActionSelectRadius, SIGNAL( triggered() ), this, SLOT( selectByRadius() ) );
connect( mActionDeselectAll, SIGNAL( triggered() ), this, SLOT( deselectAll() ) );
connect( mActionIdentify, SIGNAL( triggered() ), this, SLOT( identify() ) );
connect( mActionFeatureAction, SIGNAL( triggered() ), this, SLOT( doFeatureAction() ) );
connect( mActionMeasure, SIGNAL( triggered() ), this, SLOT( measure() ) );
connect( mActionMeasureArea, SIGNAL( triggered() ), this, SLOT( measureArea() ) );
connect( mActionMeasureAngle, SIGNAL( triggered() ), this, SLOT( measureAngle() ) );
@@ -989,6 +992,7 @@ void QgisApp::createActionGroups()
mMapToolGroup->addAction( mActionZoomIn );
mMapToolGroup->addAction( mActionZoomOut );
mMapToolGroup->addAction( mActionIdentify );
mMapToolGroup->addAction( mActionFeatureAction );
mMapToolGroup->addAction( mActionSelect );
mMapToolGroup->addAction( mActionSelectRectangle );
mMapToolGroup->addAction( mActionSelectPolygon );
@@ -1172,6 +1176,18 @@ void QgisApp::createToolBars()
selectAction->setObjectName( "ActionSelect" );
connect( bt, SIGNAL( triggered( QAction * ) ), this, SLOT( toolButtonActionTriggered( QAction * ) ) );

// feature action tool button

bt = new QToolButton( mAttributesToolBar );
bt->setPopupMode( QToolButton::MenuButtonPopup );
bt->setDefaultAction( mActionFeatureAction );
mFeatureActionMenu = new QMenu( bt );
connect( mFeatureActionMenu, SIGNAL( triggered( QAction * ) ), this, SLOT( updateDefaultFeatureAction( QAction * ) ) );
connect( mFeatureActionMenu, SIGNAL( aboutToShow() ), this, SLOT( refreshFeatureActions() ) );
bt->setMenu( mFeatureActionMenu );
QAction* featureActionAction = mAttributesToolBar->insertWidget( selectAction, bt );
featureActionAction->setObjectName( "ActionFeatureAction" );

// measure tool button

bt = new QToolButton( mAttributesToolBar );
@@ -1478,6 +1494,7 @@ void QgisApp::setTheme( QString theThemeName )
mActionZoomToLayer->setIcon( getThemeIcon( "/mActionZoomToLayer.png" ) );
mActionZoomActualSize->setIcon( getThemeIcon( "/mActionZoomActual.png" ) );
mActionIdentify->setIcon( getThemeIcon( "/mActionIdentify.png" ) );
mActionFeatureAction->setIcon( getThemeIcon( "/mAction.png" ) );
mActionSelect->setIcon( getThemeIcon( "/mActionSelect.png" ) );
mActionSelectRectangle->setIcon( getThemeIcon( "/mActionSelectRectangle.png" ) );
mActionSelectPolygon->setIcon( getThemeIcon( "/mActionSelectPolygon.png" ) );
@@ -1604,6 +1621,8 @@ void QgisApp::createCanvasTools()
mMapTools.mPan->setAction( mActionPan );
mMapTools.mIdentify = new QgsMapToolIdentify( mMapCanvas );
mMapTools.mIdentify->setAction( mActionIdentify );
mMapTools.mFeatureAction = new QgsMapToolFeatureAction( mMapCanvas );
mMapTools.mFeatureAction->setAction( mActionFeatureAction );
mMapTools.mMeasureDist = new QgsMeasureTool( mMapCanvas, false /* area */ );
mMapTools.mMeasureDist->setAction( mActionMeasure );
mMapTools.mMeasureArea = new QgsMeasureTool( mMapCanvas, true /* area */ );
@@ -3274,6 +3293,48 @@ void QgisApp::identify()
mMapCanvas->setMapTool( mMapTools.mIdentify );
}

void QgisApp::doFeatureAction()
{
mMapCanvas->setMapTool( mMapTools.mFeatureAction );
}

void QgisApp::updateDefaultFeatureAction( QAction *action )
{
foreach( QAction *a, mFeatureActionMenu->actions() )
{
a->setChecked( a == action );
}

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() );
if ( !vlayer )
return;

int index = mFeatureActionMenu->actions().indexOf( action );
vlayer->actions()->setDefaultAction( index );

doFeatureAction();
}

void QgisApp::refreshFeatureActions()
{
mFeatureActionMenu->clear();

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() );
if ( !vlayer )
return;

QgsAttributeAction *actions = vlayer->actions();
for ( int i = 0; i < actions->size(); i++ )
{
QAction *action = mFeatureActionMenu->addAction( actions->at( i ).name() );
action->setCheckable( true );
if ( i == actions->defaultAction() )
{
action->setChecked( true );
}
}
}

void QgisApp::measure()
{
mMapCanvas->setMapTool( mMapTools.mMeasureDist );
@@ -6019,7 +6080,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionLayerProperties->setEnabled( false );
mActionLayerSubsetString->setEnabled( false );
mActionAddToOverview->setEnabled( false );

mActionFeatureAction->setEnabled( false );
mActionAddFeature->setEnabled( false );
mActionMoveFeature->setEnabled( false );
mActionNodeTool->setEnabled( false );
@@ -6077,6 +6138,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionLayerSaveAs->setEnabled( true );
mActionLayerSelectionSaveAs->setEnabled( true );
mActionCopyFeatures->setEnabled( layerHasSelection );
mActionFeatureAction->setEnabled( vlayer->actions()->size() > 0 );

if ( !vlayer->isEditable() && mMapCanvas->mapTool() && mMapCanvas->mapTool()->isEditTool() )
{
@@ -6244,6 +6306,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionFullHistogramStretch->setEnabled( false );
}
mActionLayerSubsetString->setEnabled( false );
mActionFeatureAction->setEnabled( false );
mActionSelect->setEnabled( false );
mActionSelectRectangle->setEnabled( false );
mActionSelectPolygon->setEnabled( false );
@@ -246,6 +246,7 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
QAction *actionSelectFreehand() { return mActionSelectFreehand; }
QAction *actionSelectRadius() { return mActionSelectRadius; }
QAction *actionIdentify() { return mActionIdentify; }
QAction *actionFeatureAction() { return mActionFeatureAction; }
QAction *actionMeasure() { return mActionMeasure; }
QAction *actionMeasureArea() { return mActionMeasureArea; }
QAction *actionZoomFullExtent() { return mActionZoomFullExtent; }
@@ -757,6 +758,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
//! Measure angle
void measureAngle();

//! Run the default feature action on the current layer
void doFeatureAction();
//! Set the default feature action for the current layer
void updateDefaultFeatureAction( QAction *action );
//! Refresh the list of feature actions of the current layer
void refreshFeatureActions();

//annotations
void addFormAnnotation();
void addTextAnnotation();
@@ -969,6 +977,7 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
QgsMapTool* mZoomOut;
QgsMapTool* mPan;
QgsMapTool* mIdentify;
QgsMapTool* mFeatureAction;
QgsMapTool* mMeasureDist;
QgsMapTool* mMeasureArea;
QgsMapTool* mMeasureAngle;
@@ -1025,6 +1034,8 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
QLabel * mOnTheFlyProjectionStatusLabel;
//! Widget in status bar used to show status of on the fly projection
QToolButton * mOnTheFlyProjectionStatusButton;
//! Menu that contains the list of actions of the selected vector layer
QMenu *mFeatureActionMenu;
//! Popup menu
QMenu * mPopupMenu;
//! Top level database menu
@@ -421,6 +421,7 @@ QAction *QgisAppInterface::actionSelectPolygon() { return qgis->actionSelectPoly
QAction *QgisAppInterface::actionSelectFreehand() { return qgis->actionSelectFreehand(); }
QAction *QgisAppInterface::actionSelectRadius() { return qgis->actionSelectRadius(); }
QAction *QgisAppInterface::actionIdentify() { return qgis->actionIdentify(); }
QAction *QgisAppInterface::actionFeatureAction() { return qgis->actionFeatureAction(); }
QAction *QgisAppInterface::actionMeasure() { return qgis->actionMeasure(); }
QAction *QgisAppInterface::actionMeasureArea() { return qgis->actionMeasureArea(); }
QAction *QgisAppInterface::actionViewSeparator1() { return 0; }
@@ -269,6 +269,7 @@ class QgisAppInterface : public QgisInterface
virtual QAction *actionSelectFreehand();
virtual QAction *actionSelectRadius();
virtual QAction *actionIdentify();
virtual QAction *actionFeatureAction();
virtual QAction *actionMeasure();
virtual QAction *actionMeasureArea();
virtual QAction *actionViewSeparator1();
@@ -0,0 +1,156 @@
/***************************************************************************
qgsmaptoolfeatureaction.h - map tool for running feature actions
---------------------
begin : January 2012
copyright : (C) 2012 by Giuseppe Sucameli
email : brush.tyler 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. *
* *
***************************************************************************/

#include "qgsmaptoolfeatureaction.h"

#include "qgsdistancearea.h"
#include "qgsfeature.h"
#include "qgsfield.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmaptopixel.h"
#include "qgsmessageviewer.h"
#include "qgsattributeaction.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsmaplayerregistry.h"
#include "qgisapp.h"

#include <QSettings>
#include <QMessageBox>
#include <QMouseEvent>
#include <QStatusBar>

QgsMapToolFeatureAction::QgsMapToolFeatureAction( QgsMapCanvas* canvas )
: QgsMapTool( canvas )
{
}

QgsMapToolFeatureAction::~QgsMapToolFeatureAction()
{
}

void QgsMapToolFeatureAction::canvasMoveEvent( QMouseEvent *e )
{
Q_UNUSED( e );
}

void QgsMapToolFeatureAction::canvasPressEvent( QMouseEvent *e )
{
Q_UNUSED( e );
}

void QgsMapToolFeatureAction::canvasReleaseEvent( QMouseEvent *e )
{
if ( !mCanvas || mCanvas->isDrawing() )
{
return;
}

QgsMapLayer *layer = mCanvas->currentLayer();

if ( !layer || layer->type() != QgsMapLayer::VectorLayer )
{
QMessageBox::warning( mCanvas,
tr( "No active vector layer" ),
tr( "To run an action, you must choose a vector layer by clicking on its name in the legend" ) );
return;
}

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vlayer->actions()->size() == 0 )
{
QMessageBox::warning( mCanvas,
tr( "No actions available" ),
tr( "The active vector layer has no defined actions" ) );
return;
}

if ( !doAction( vlayer, e->x(), e->y() ) )
QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) );
}

void QgsMapToolFeatureAction::activate()
{
QgsMapTool::activate();
}

void QgsMapToolFeatureAction::deactivate()
{
QgsMapTool::deactivate();
}

bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y )
{
if ( !layer )
return false;

QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );

// 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();

if ( identifyValue <= 0.0 )
identifyValue = QGis::DEFAULT_IDENTIFY_RADIUS;

QgsFeature feat;

// toLayerCoordinates will throw an exception for an 'invalid' point.
// For example, if you project a world map onto a globe using EPSG 2163
// and then click somewhere off the globe, an exception will be thrown.
try
{
// create the search rectangle
double searchRadius = mCanvas->extent().width() * ( identifyValue / 100.0 );

QgsRectangle 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 );

layer->select( layer->pendingAllAttributesList(), r, true, true );
QgsFeature f;
if ( layer->nextFeature( f ) )
feat = QgsFeature( f );
else
return false;
}
catch ( QgsCsException & cse )
{
Q_UNUSED( cse );
// catch exception for 'invalid' point and proceed with no features found
QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) );
}

int action = layer->actions()->defaultAction();

// define custom substitutions: layer id and clicked coords
QMap<QString, QVariant> substitutionMap;
substitutionMap.insert( "$layerid", layer->id() );
point = toLayerCoordinates( layer, point );
substitutionMap.insert( "$clickx", point.x() );
substitutionMap.insert( "$clicky", point.y() );

layer->actions()->doAction( action, feat, &substitutionMap );
return true;
}

0 comments on commit acc1d55

Please sign in to comment.
You can’t perform that action at this time.