Skip to content

Commit dbb48f2

Browse files
committed
[FEATURE] fill ring digitinzing tool
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.
1 parent cf39d71 commit dbb48f2

8 files changed

+267
-2
lines changed

images/images.qrc

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
<file>themes/default/mActionAddPart.png</file>
133133
<file>themes/default/mActionAddRasterLayer.svg</file>
134134
<file>themes/default/mActionAddRing.png</file>
135+
<file>themes/default/mActionFillRing.png</file>
135136
<file>themes/default/mActionAddSpatiaLiteLayer.svg</file>
136137
<file>themes/default/mActionAddVertex.png</file>
137138
<file>themes/default/mActionAddWcsLayer.svg</file>
1.23 KB
Loading

src/app/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ SET(QGIS_APP_SRCS
4949
qgsmaptooladdfeature.cpp
5050
qgsmaptooladdpart.cpp
5151
qgsmaptooladdring.cpp
52+
qgsmaptoolfillring.cpp
5253
qgsmaptoolannotation.cpp
5354
qgsmaptoolcapture.cpp
5455
qgsmaptoolchangelabelproperties.cpp
@@ -210,6 +211,7 @@ SET (QGIS_APP_MOC_HDRS
210211
qgsmaptoolcapture.h
211212
qgsmaptooladdpart.h
212213
qgsmaptooladdring.h
214+
qgsmaptoolfillring.h
213215
qgsmaptoolchangelabelproperties.h
214216
qgsmaptooldeletepart.h
215217
qgsmaptooldeletering.h

src/app/qgisapp.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225
#include "qgsmaptooladdfeature.h"
226226
#include "qgsmaptooladdpart.h"
227227
#include "qgsmaptooladdring.h"
228+
#include "qgsmaptoolfillring.h"
228229
#include "qgsmaptoolannotation.h"
229230
#include "qgsmaptooldeletering.h"
230231
#include "qgsmaptooldeletepart.h"
@@ -793,6 +794,7 @@ QgisApp::~QgisApp()
793794
delete mMapTools.mAddFeature;
794795
delete mMapTools.mAddPart;
795796
delete mMapTools.mAddRing;
797+
delete mMapTools.mFillRing;
796798
delete mMapTools.mAnnotation;
797799
delete mMapTools.mChangeLabelProperties;
798800
delete mMapTools.mDeletePart;
@@ -982,6 +984,7 @@ void QgisApp::createActions()
982984
connect( mActionSplitParts, SIGNAL( triggered() ), this, SLOT( splitParts() ) );
983985
connect( mActionDeleteSelected, SIGNAL( triggered() ), this, SLOT( deleteSelected() ) );
984986
connect( mActionAddRing, SIGNAL( triggered() ), this, SLOT( addRing() ) );
987+
connect( mActionFillRing, SIGNAL( triggered() ), this, SLOT( fillRing() ) );
985988
connect( mActionAddPart, SIGNAL( triggered() ), this, SLOT( addPart() ) );
986989
connect( mActionSimplifyFeature, SIGNAL( triggered() ), this, SLOT( simplifyFeature() ) );
987990
connect( mActionDeleteRing, SIGNAL( triggered() ), this, SLOT( deleteRing() ) );
@@ -1253,6 +1256,7 @@ void QgisApp::createActionGroups()
12531256
mMapToolGroup->addAction( mActionSplitParts );
12541257
mMapToolGroup->addAction( mActionDeleteSelected );
12551258
mMapToolGroup->addAction( mActionAddRing );
1259+
mMapToolGroup->addAction( mActionFillRing );
12561260
mMapToolGroup->addAction( mActionAddPart );
12571261
mMapToolGroup->addAction( mActionSimplifyFeature );
12581262
mMapToolGroup->addAction( mActionDeleteRing );
@@ -1799,6 +1803,7 @@ void QgisApp::setTheme( QString theThemeName )
17991803
mActionUndo->setIcon( QgsApplication::getThemeIcon( "/mActionUndo.png" ) );
18001804
mActionRedo->setIcon( QgsApplication::getThemeIcon( "/mActionRedo.png" ) );
18011805
mActionAddRing->setIcon( QgsApplication::getThemeIcon( "/mActionAddRing.png" ) );
1806+
mActionFillRing->setIcon( QgsApplication::getThemeIcon( "/mActionFillRing.png" ) );
18021807
mActionAddPart->setIcon( QgsApplication::getThemeIcon( "/mActionAddPart.png" ) );
18031808
mActionDeleteRing->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteRing.png" ) );
18041809
mActionDeletePart->setIcon( QgsApplication::getThemeIcon( "/mActionDeletePart.png" ) );
@@ -2030,6 +2035,8 @@ void QgisApp::createCanvasTools()
20302035
mMapTools.mSelectRadius->setAction( mActionSelectRadius );
20312036
mMapTools.mAddRing = new QgsMapToolAddRing( mMapCanvas );
20322037
mMapTools.mAddRing->setAction( mActionAddRing );
2038+
mMapTools.mFillRing = new QgsMapToolFillRing( mMapCanvas );
2039+
mMapTools.mFillRing->setAction( mActionFillRing );
20332040
mMapTools.mAddPart = new QgsMapToolAddPart( mMapCanvas );
20342041
mMapTools.mSimplifyFeature = new QgsMapToolSimplify( mMapCanvas );
20352042
mMapTools.mSimplifyFeature->setAction( mActionSimplifyFeature );
@@ -5573,6 +5580,16 @@ void QgisApp::addRing()
55735580
mMapCanvas->setMapTool( mMapTools.mAddRing );
55745581
}
55755582

5583+
void QgisApp::fillRing()
5584+
{
5585+
if ( mMapCanvas && mMapCanvas->isDrawing() )
5586+
{
5587+
return;
5588+
}
5589+
mMapCanvas->setMapTool( mMapTools.mFillRing );
5590+
}
5591+
5592+
55765593
void QgisApp::addPart()
55775594
{
55785595
if ( mMapCanvas && mMapCanvas->isDrawing() )
@@ -8382,6 +8399,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
83828399
mActionRedo->setEnabled( false );
83838400
mActionSimplifyFeature->setEnabled( false );
83848401
mActionAddRing->setEnabled( false );
8402+
mActionFillRing->setEnabled( false );
83858403
mActionAddPart->setEnabled( false );
83868404
mActionDeleteRing->setEnabled( false );
83878405
mActionDeletePart->setEnabled( false );
@@ -8514,6 +8532,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
85148532
mActionAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionCapturePoint.png" ) );
85158533

85168534
mActionAddRing->setEnabled( false );
8535+
mActionFillRing->setEnabled( false );
85178536
mActionReshapeFeatures->setEnabled( false );
85188537
mActionSplitFeatures->setEnabled( false );
85198538
mActionSplitParts->setEnabled( false );
@@ -8541,13 +8560,15 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
85418560
mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes );
85428561

85438562
mActionAddRing->setEnabled( false );
8563+
mActionFillRing->setEnabled( false );
85448564
mActionDeleteRing->setEnabled( false );
85458565
}
85468566
else if ( vlayer->geometryType() == QGis::Polygon )
85478567
{
85488568
mActionAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionCapturePolygon.png" ) );
85498569

85508570
mActionAddRing->setEnabled( isEditable && canAddFeatures );
8571+
mActionFillRing->setEnabled( isEditable && canAddFeatures );
85518572
mActionReshapeFeatures->setEnabled( isEditable && canAddFeatures );
85528573
mActionSplitFeatures->setEnabled( isEditable && canAddFeatures );
85538574
mActionSplitParts->setEnabled( isEditable && canAddFeatures );
@@ -8621,6 +8642,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
86218642
mActionAddFeature->setEnabled( false );
86228643
mActionDeleteSelected->setEnabled( false );
86238644
mActionAddRing->setEnabled( false );
8645+
mActionFillRing->setEnabled( false );
86248646
mActionAddPart->setEnabled( false );
86258647
mActionNodeTool->setEnabled( false );
86268648
mActionMoveFeature->setEnabled( false );

src/app/qgisapp.h

+4
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
272272
QAction *actionSplitFeatures() { return mActionSplitFeatures; }
273273
QAction *actionSplitParts() { return mActionSplitParts; }
274274
QAction *actionAddRing() { return mActionAddRing; }
275+
QAction *actionFillRing() { return mActionFillRing; }
275276
QAction *actionAddPart() { return mActionAddPart; }
276277
QAction *actionSimplifyFeature() { return mActionSimplifyFeature; }
277278
QAction *actionDeleteRing() { return mActionDeleteRing; }
@@ -937,6 +938,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
937938
void splitParts();
938939
//! activates the add ring tool
939940
void addRing();
941+
//! activates the fill ring tool
942+
void fillRing();
940943
//! activates the add part tool
941944
void addPart();
942945
//! simplifies feature
@@ -1358,6 +1361,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
13581361
QgsMapTool* mVertexMove;
13591362
QgsMapTool* mVertexDelete;
13601363
QgsMapTool* mAddRing;
1364+
QgsMapTool* mFillRing;
13611365
QgsMapTool* mAddPart;
13621366
QgsMapTool* mSimplifyFeature;
13631367
QgsMapTool* mDeleteRing;

src/app/qgsmaptoolfillring.cpp

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/***************************************************************************
2+
qgsmaptoolfillring.h - map tool to cut rings in polygon and multipolygon
3+
features and fill them with new feature
4+
---------------------
5+
begin : December 2013
6+
copyright : (C) 2013 by Alexander Bruy
7+
email : alexander dot bruy at gmail dot com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
#include "qgsmaptoolfillring.h"
18+
#include "qgsgeometry.h"
19+
#include "qgsmapcanvas.h"
20+
#include "qgsvectorlayer.h"
21+
#include "qgsattributedialog.h"
22+
#include <qgsapplication.h>
23+
24+
#include <QMessageBox>
25+
#include <QMouseEvent>
26+
27+
#include <limits>
28+
29+
QgsMapToolFillRing::QgsMapToolFillRing( QgsMapCanvas* canvas ): QgsMapToolCapture( canvas, QgsMapToolCapture::CapturePolygon )
30+
{
31+
32+
}
33+
34+
QgsMapToolFillRing::~QgsMapToolFillRing()
35+
{
36+
37+
}
38+
39+
void QgsMapToolFillRing::canvasReleaseEvent( QMouseEvent * e )
40+
{
41+
//check if we operate on a vector layer
42+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
43+
44+
if ( !vlayer )
45+
{
46+
notifyNotVectorLayer();
47+
return;
48+
}
49+
50+
if ( !vlayer->isEditable() )
51+
{
52+
notifyNotEditableLayer();
53+
return;
54+
}
55+
56+
//add point to list and to rubber band
57+
if ( e->button() == Qt::LeftButton )
58+
{
59+
int error = addVertex( e->pos() );
60+
if ( error == 1 )
61+
{
62+
//current layer is not a vector layer
63+
return;
64+
}
65+
else if ( error == 2 )
66+
{
67+
//problem with coordinate transformation
68+
QMessageBox::information( 0, tr( "Coordinate transform error" ),
69+
tr( "Cannot transform the point to the layers coordinate system" ) );
70+
return;
71+
}
72+
73+
startCapturing();
74+
}
75+
else if ( e->button() == Qt::RightButton )
76+
{
77+
deleteTempRubberBand();
78+
79+
closePolygon();
80+
81+
vlayer->beginEditCommand( tr( "Ring added and filled" ) );
82+
int addRingReturnCode = vlayer->addRing( points() );
83+
if ( addRingReturnCode != 0 )
84+
{
85+
QString errorMessage;
86+
//todo: open message box to communicate errors
87+
if ( addRingReturnCode == 1 )
88+
{
89+
errorMessage = tr( "A problem with geometry type occured" );
90+
}
91+
else if ( addRingReturnCode == 2 )
92+
{
93+
errorMessage = tr( "The inserted Ring is not closed" );
94+
}
95+
else if ( addRingReturnCode == 3 )
96+
{
97+
errorMessage = tr( "The inserted Ring is not a valid geometry" );
98+
}
99+
else if ( addRingReturnCode == 4 )
100+
{
101+
errorMessage = tr( "The inserted Ring crosses existing rings" );
102+
}
103+
else if ( addRingReturnCode == 5 )
104+
{
105+
errorMessage = tr( "The inserted Ring is not contained in a feature" );
106+
}
107+
else
108+
{
109+
errorMessage = tr( "An unknown error occured" );
110+
}
111+
QMessageBox::critical( 0, tr( "Error, could not add ring" ), errorMessage );
112+
vlayer->destroyEditCommand();
113+
}
114+
else
115+
{
116+
// find parent feature and get it attributes
117+
double xMin, xMax, yMin, yMax;
118+
QgsRectangle bBox;
119+
120+
xMin = std::numeric_limits<double>::max();
121+
xMax = -std::numeric_limits<double>::max();
122+
yMin = std::numeric_limits<double>::max();
123+
yMax = -std::numeric_limits<double>::max();
124+
125+
for ( QList<QgsPoint>::const_iterator it = points().constBegin(); it != points().constEnd(); ++it )
126+
{
127+
if ( it->x() < xMin )
128+
{
129+
xMin = it->x();
130+
}
131+
if ( it->x() > xMax )
132+
{
133+
xMax = it->x();
134+
}
135+
if ( it->y() < yMin )
136+
{
137+
yMin = it->y();
138+
}
139+
if ( it->y() > yMax )
140+
{
141+
yMax = it->y();
142+
}
143+
}
144+
bBox.setXMinimum( xMin );
145+
bBox.setYMinimum( yMin );
146+
bBox.setXMaximum( xMax );
147+
bBox.setYMaximum( yMax );
148+
149+
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
150+
151+
QgsFeature f;
152+
bool res = false;
153+
while ( fit.nextFeature( f ) )
154+
{
155+
//create QgsFeature with wkb representation
156+
QgsFeature* ft = new QgsFeature( vlayer->pendingFields(), 0 );
157+
158+
QgsGeometry *g;
159+
g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() );
160+
ft->setGeometry( g );
161+
ft->setAttributes( f.attributes() );
162+
163+
if ( QgsApplication::keyboardModifiers() == Qt::ControlModifier )
164+
{
165+
res = vlayer->addFeature( *ft );
166+
}
167+
else
168+
{
169+
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, NULL, true );
170+
if ( dialog->exec() )
171+
{
172+
res = vlayer->addFeature( *ft );
173+
}
174+
}
175+
176+
if ( res )
177+
{
178+
vlayer->endEditCommand();
179+
}
180+
else
181+
{
182+
delete ft;
183+
vlayer->destroyEditCommand();
184+
}
185+
res = false;
186+
}
187+
}
188+
stopCapturing();
189+
}
190+
}

src/app/qgsmaptoolfillring.h

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/***************************************************************************
2+
qgsmaptoolfillring.h - map tool to cut rings in polygon and multipolygon
3+
features and fill them with new feature
4+
---------------------
5+
begin : December 2013
6+
copyright : (C) 2013 by Alexander Bruy
7+
email : alexander dot bruy at gmail dot com
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
#include "qgsmaptoolcapture.h"
18+
19+
/** A tool to cut holes into polygon and multipolygon features and fill them
20+
* with new feature. Attributes are copied from parent feature.
21+
* */
22+
class APP_EXPORT QgsMapToolFillRing: public QgsMapToolCapture
23+
{
24+
Q_OBJECT
25+
public:
26+
QgsMapToolFillRing( QgsMapCanvas* canvas );
27+
virtual ~QgsMapToolFillRing();
28+
void canvasReleaseEvent( QMouseEvent * e );
29+
};

0 commit comments

Comments
 (0)