Skip to content

Commit

Permalink
[FEATURE] fill ring digitinzing tool
Browse files Browse the repository at this point in the history
Used to cut holes in polygons and automatically fill them with new
features. Attributes taken from parent feature. Feature form opens
by default. Hold Ctrl key to suppress it.
  • Loading branch information
alexbruy committed Dec 17, 2013
1 parent cf39d71 commit dbb48f2
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 2 deletions.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -132,6 +132,7 @@
<file>themes/default/mActionAddPart.png</file>
<file>themes/default/mActionAddRasterLayer.svg</file>
<file>themes/default/mActionAddRing.png</file>
<file>themes/default/mActionFillRing.png</file>
<file>themes/default/mActionAddSpatiaLiteLayer.svg</file>
<file>themes/default/mActionAddVertex.png</file>
<file>themes/default/mActionAddWcsLayer.svg</file>
Expand Down
Binary file added images/themes/default/mActionFillRing.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -49,6 +49,7 @@ SET(QGIS_APP_SRCS
qgsmaptooladdfeature.cpp
qgsmaptooladdpart.cpp
qgsmaptooladdring.cpp
qgsmaptoolfillring.cpp
qgsmaptoolannotation.cpp
qgsmaptoolcapture.cpp
qgsmaptoolchangelabelproperties.cpp
Expand Down Expand Up @@ -210,6 +211,7 @@ SET (QGIS_APP_MOC_HDRS
qgsmaptoolcapture.h
qgsmaptooladdpart.h
qgsmaptooladdring.h
qgsmaptoolfillring.h
qgsmaptoolchangelabelproperties.h
qgsmaptooldeletepart.h
qgsmaptooldeletering.h
Expand Down
22 changes: 22 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -225,6 +225,7 @@
#include "qgsmaptooladdfeature.h"
#include "qgsmaptooladdpart.h"
#include "qgsmaptooladdring.h"
#include "qgsmaptoolfillring.h"
#include "qgsmaptoolannotation.h"
#include "qgsmaptooldeletering.h"
#include "qgsmaptooldeletepart.h"
Expand Down Expand Up @@ -793,6 +794,7 @@ QgisApp::~QgisApp()
delete mMapTools.mAddFeature;
delete mMapTools.mAddPart;
delete mMapTools.mAddRing;
delete mMapTools.mFillRing;
delete mMapTools.mAnnotation;
delete mMapTools.mChangeLabelProperties;
delete mMapTools.mDeletePart;
Expand Down Expand Up @@ -982,6 +984,7 @@ void QgisApp::createActions()
connect( mActionSplitParts, SIGNAL( triggered() ), this, SLOT( splitParts() ) );
connect( mActionDeleteSelected, SIGNAL( triggered() ), this, SLOT( deleteSelected() ) );
connect( mActionAddRing, SIGNAL( triggered() ), this, SLOT( addRing() ) );
connect( mActionFillRing, SIGNAL( triggered() ), this, SLOT( fillRing() ) );
connect( mActionAddPart, SIGNAL( triggered() ), this, SLOT( addPart() ) );
connect( mActionSimplifyFeature, SIGNAL( triggered() ), this, SLOT( simplifyFeature() ) );
connect( mActionDeleteRing, SIGNAL( triggered() ), this, SLOT( deleteRing() ) );
Expand Down Expand Up @@ -1253,6 +1256,7 @@ void QgisApp::createActionGroups()
mMapToolGroup->addAction( mActionSplitParts );
mMapToolGroup->addAction( mActionDeleteSelected );
mMapToolGroup->addAction( mActionAddRing );
mMapToolGroup->addAction( mActionFillRing );
mMapToolGroup->addAction( mActionAddPart );
mMapToolGroup->addAction( mActionSimplifyFeature );
mMapToolGroup->addAction( mActionDeleteRing );
Expand Down Expand Up @@ -1799,6 +1803,7 @@ void QgisApp::setTheme( QString theThemeName )
mActionUndo->setIcon( QgsApplication::getThemeIcon( "/mActionUndo.png" ) );
mActionRedo->setIcon( QgsApplication::getThemeIcon( "/mActionRedo.png" ) );
mActionAddRing->setIcon( QgsApplication::getThemeIcon( "/mActionAddRing.png" ) );
mActionFillRing->setIcon( QgsApplication::getThemeIcon( "/mActionFillRing.png" ) );
mActionAddPart->setIcon( QgsApplication::getThemeIcon( "/mActionAddPart.png" ) );
mActionDeleteRing->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteRing.png" ) );
mActionDeletePart->setIcon( QgsApplication::getThemeIcon( "/mActionDeletePart.png" ) );
Expand Down Expand Up @@ -2030,6 +2035,8 @@ void QgisApp::createCanvasTools()
mMapTools.mSelectRadius->setAction( mActionSelectRadius );
mMapTools.mAddRing = new QgsMapToolAddRing( mMapCanvas );
mMapTools.mAddRing->setAction( mActionAddRing );
mMapTools.mFillRing = new QgsMapToolFillRing( mMapCanvas );
mMapTools.mFillRing->setAction( mActionFillRing );
mMapTools.mAddPart = new QgsMapToolAddPart( mMapCanvas );
mMapTools.mSimplifyFeature = new QgsMapToolSimplify( mMapCanvas );
mMapTools.mSimplifyFeature->setAction( mActionSimplifyFeature );
Expand Down Expand Up @@ -5573,6 +5580,16 @@ void QgisApp::addRing()
mMapCanvas->setMapTool( mMapTools.mAddRing );
}

void QgisApp::fillRing()
{
if ( mMapCanvas && mMapCanvas->isDrawing() )
{
return;
}
mMapCanvas->setMapTool( mMapTools.mFillRing );
}


void QgisApp::addPart()
{
if ( mMapCanvas && mMapCanvas->isDrawing() )
Expand Down Expand Up @@ -8382,6 +8399,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionRedo->setEnabled( false );
mActionSimplifyFeature->setEnabled( false );
mActionAddRing->setEnabled( false );
mActionFillRing->setEnabled( false );
mActionAddPart->setEnabled( false );
mActionDeleteRing->setEnabled( false );
mActionDeletePart->setEnabled( false );
Expand Down Expand Up @@ -8514,6 +8532,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionCapturePoint.png" ) );

mActionAddRing->setEnabled( false );
mActionFillRing->setEnabled( false );
mActionReshapeFeatures->setEnabled( false );
mActionSplitFeatures->setEnabled( false );
mActionSplitParts->setEnabled( false );
Expand Down Expand Up @@ -8541,13 +8560,15 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes );

mActionAddRing->setEnabled( false );
mActionFillRing->setEnabled( false );
mActionDeleteRing->setEnabled( false );
}
else if ( vlayer->geometryType() == QGis::Polygon )
{
mActionAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionCapturePolygon.png" ) );

mActionAddRing->setEnabled( isEditable && canAddFeatures );
mActionFillRing->setEnabled( isEditable && canAddFeatures );
mActionReshapeFeatures->setEnabled( isEditable && canAddFeatures );
mActionSplitFeatures->setEnabled( isEditable && canAddFeatures );
mActionSplitParts->setEnabled( isEditable && canAddFeatures );
Expand Down Expand Up @@ -8621,6 +8642,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionAddFeature->setEnabled( false );
mActionDeleteSelected->setEnabled( false );
mActionAddRing->setEnabled( false );
mActionFillRing->setEnabled( false );
mActionAddPart->setEnabled( false );
mActionNodeTool->setEnabled( false );
mActionMoveFeature->setEnabled( false );
Expand Down
4 changes: 4 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -272,6 +272,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QAction *actionSplitFeatures() { return mActionSplitFeatures; }
QAction *actionSplitParts() { return mActionSplitParts; }
QAction *actionAddRing() { return mActionAddRing; }
QAction *actionFillRing() { return mActionFillRing; }
QAction *actionAddPart() { return mActionAddPart; }
QAction *actionSimplifyFeature() { return mActionSimplifyFeature; }
QAction *actionDeleteRing() { return mActionDeleteRing; }
Expand Down Expand Up @@ -937,6 +938,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void splitParts();
//! activates the add ring tool
void addRing();
//! activates the fill ring tool
void fillRing();
//! activates the add part tool
void addPart();
//! simplifies feature
Expand Down Expand Up @@ -1358,6 +1361,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QgsMapTool* mVertexMove;
QgsMapTool* mVertexDelete;
QgsMapTool* mAddRing;
QgsMapTool* mFillRing;
QgsMapTool* mAddPart;
QgsMapTool* mSimplifyFeature;
QgsMapTool* mDeleteRing;
Expand Down
190 changes: 190 additions & 0 deletions src/app/qgsmaptoolfillring.cpp
@@ -0,0 +1,190 @@
/***************************************************************************
qgsmaptoolfillring.h - map tool to cut rings in polygon and multipolygon
features and fill them with new feature
---------------------
begin : December 2013
copyright : (C) 2013 by Alexander Bruy
email : alexander dot bruy 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 "qgsmaptoolfillring.h"
#include "qgsgeometry.h"
#include "qgsmapcanvas.h"
#include "qgsvectorlayer.h"
#include "qgsattributedialog.h"
#include <qgsapplication.h>

#include <QMessageBox>
#include <QMouseEvent>

#include <limits>

QgsMapToolFillRing::QgsMapToolFillRing( QgsMapCanvas* canvas ): QgsMapToolCapture( canvas, QgsMapToolCapture::CapturePolygon )
{

}

QgsMapToolFillRing::~QgsMapToolFillRing()
{

}

void QgsMapToolFillRing::canvasReleaseEvent( QMouseEvent * e )
{
//check if we operate on a vector layer
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );

if ( !vlayer )
{
notifyNotVectorLayer();
return;
}

if ( !vlayer->isEditable() )
{
notifyNotEditableLayer();
return;
}

//add point to list and to rubber band
if ( e->button() == Qt::LeftButton )
{
int error = addVertex( e->pos() );
if ( error == 1 )
{
//current layer is not a vector layer
return;
}
else if ( error == 2 )
{
//problem with coordinate transformation
QMessageBox::information( 0, tr( "Coordinate transform error" ),
tr( "Cannot transform the point to the layers coordinate system" ) );
return;
}

startCapturing();
}
else if ( e->button() == Qt::RightButton )
{
deleteTempRubberBand();

closePolygon();

vlayer->beginEditCommand( tr( "Ring added and filled" ) );
int addRingReturnCode = vlayer->addRing( points() );
if ( addRingReturnCode != 0 )
{
QString errorMessage;
//todo: open message box to communicate errors
if ( addRingReturnCode == 1 )
{
errorMessage = tr( "A problem with geometry type occured" );
}
else if ( addRingReturnCode == 2 )
{
errorMessage = tr( "The inserted Ring is not closed" );
}
else if ( addRingReturnCode == 3 )
{
errorMessage = tr( "The inserted Ring is not a valid geometry" );
}
else if ( addRingReturnCode == 4 )
{
errorMessage = tr( "The inserted Ring crosses existing rings" );
}
else if ( addRingReturnCode == 5 )
{
errorMessage = tr( "The inserted Ring is not contained in a feature" );
}
else
{
errorMessage = tr( "An unknown error occured" );
}
QMessageBox::critical( 0, tr( "Error, could not add ring" ), errorMessage );
vlayer->destroyEditCommand();
}
else
{
// find parent feature and get it attributes
double xMin, xMax, yMin, yMax;
QgsRectangle bBox;

xMin = std::numeric_limits<double>::max();
xMax = -std::numeric_limits<double>::max();
yMin = std::numeric_limits<double>::max();
yMax = -std::numeric_limits<double>::max();

for ( QList<QgsPoint>::const_iterator it = points().constBegin(); it != points().constEnd(); ++it )
{
if ( it->x() < xMin )
{
xMin = it->x();
}
if ( it->x() > xMax )
{
xMax = it->x();
}
if ( it->y() < yMin )
{
yMin = it->y();
}
if ( it->y() > yMax )
{
yMax = it->y();
}
}
bBox.setXMinimum( xMin );
bBox.setYMinimum( yMin );
bBox.setXMaximum( xMax );
bBox.setYMaximum( yMax );

QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );

QgsFeature f;
bool res = false;
while ( fit.nextFeature( f ) )
{
//create QgsFeature with wkb representation
QgsFeature* ft = new QgsFeature( vlayer->pendingFields(), 0 );

QgsGeometry *g;
g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() );
ft->setGeometry( g );
ft->setAttributes( f.attributes() );

if ( QgsApplication::keyboardModifiers() == Qt::ControlModifier )
{
res = vlayer->addFeature( *ft );
}
else
{
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, NULL, true );
if ( dialog->exec() )
{
res = vlayer->addFeature( *ft );
}
}

if ( res )
{
vlayer->endEditCommand();
}
else
{
delete ft;
vlayer->destroyEditCommand();
}
res = false;
}
}
stopCapturing();
}
}
29 changes: 29 additions & 0 deletions src/app/qgsmaptoolfillring.h
@@ -0,0 +1,29 @@
/***************************************************************************
qgsmaptoolfillring.h - map tool to cut rings in polygon and multipolygon
features and fill them with new feature
---------------------
begin : December 2013
copyright : (C) 2013 by Alexander Bruy
email : alexander dot bruy 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 "qgsmaptoolcapture.h"

/** A tool to cut holes into polygon and multipolygon features and fill them
* with new feature. Attributes are copied from parent feature.
* */
class APP_EXPORT QgsMapToolFillRing: public QgsMapToolCapture
{
Q_OBJECT
public:
QgsMapToolFillRing( QgsMapCanvas* canvas );
virtual ~QgsMapToolFillRing();
void canvasReleaseEvent( QMouseEvent * e );
};

0 comments on commit dbb48f2

Please sign in to comment.