Skip to content

Commit e7744bd

Browse files
committedJul 20, 2017
[FEATURE] fill ring tool: by pressing shift an existing ring is filled
1 parent 0fbc189 commit e7744bd

File tree

2 files changed

+122
-55
lines changed

2 files changed

+122
-55
lines changed
 

‎src/app/qgsmaptoolfillring.cpp

Lines changed: 115 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
#include "qgsattributedialog.h"
2323
#include "qgisapp.h"
2424
#include "qgsvectorlayerutils.h"
25-
#include <QMouseEvent>
2625

26+
#include <QMouseEvent>
2727
#include <limits>
2828

2929
QgsMapToolFillRing::QgsMapToolFillRing( QgsMapCanvas *canvas )
@@ -52,38 +52,49 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
5252
return;
5353
}
5454

55-
//add point to list and to rubber band
56-
if ( e->button() == Qt::LeftButton )
55+
if ( e->button() == Qt::LeftButton && QApplication::keyboardModifiers() == Qt::ShiftModifier && !isCapturing() )
56+
{
57+
// left button with shift fills an existing ring
58+
}
59+
else if ( e->button() == Qt::LeftButton )
5760
{
61+
// add point to list and to rubber band
62+
5863
int error = addVertex( e->mapPoint() );
5964
if ( error == 1 )
6065
{
61-
//current layer is not a vector layer
66+
// current layer is not a vector layer
6267
return;
6368
}
6469
else if ( error == 2 )
6570
{
66-
//problem with coordinate transformation
71+
// problem with coordinate transformation
6772
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
6873
return;
6974
}
7075

7176
startCapturing();
77+
return;
7278
}
73-
else if ( e->button() == Qt::RightButton )
79+
else if ( e->button() != Qt::RightButton || !isCapturing() )
7480
{
75-
if ( !isCapturing() )
76-
return;
81+
return;
82+
}
83+
84+
QgsGeometry g;
85+
QgsFeatureId fid;
7786

87+
if ( isCapturing() )
88+
{
7889
deleteTempRubberBand();
7990

8091
closePolygon();
8192

8293
vlayer->beginEditCommand( tr( "Ring added and filled" ) );
94+
8395
QList< QgsPointXY > pointList = points();
8496

85-
QgsFeatureId modifiedFid;
86-
int addRingReturnCode = vlayer->addRing( pointList, &modifiedFid );
97+
int addRingReturnCode = vlayer->addRing( pointList, &fid );
8798
if ( addRingReturnCode != 0 )
8899
{
89100
QString errorMessage;
@@ -114,65 +125,114 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
114125
}
115126
emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL );
116127
vlayer->destroyEditCommand();
128+
129+
return;
117130
}
118-
else
131+
132+
g = QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() );
133+
}
134+
else
135+
{
136+
vlayer->beginEditCommand( tr( "Ring filled" ) );
137+
138+
g = ringUnderPoint( e->mapPoint(), fid );
139+
140+
if ( fid == -1 )
119141
{
120-
// find parent feature and get it attributes
121-
double xMin, xMax, yMin, yMax;
122-
QgsRectangle bBox;
142+
emit messageEmitted( tr( "No ring found to fill." ), QgsMessageBar::CRITICAL );
143+
vlayer->destroyEditCommand();
144+
return;
145+
}
146+
}
123147

124-
xMin = std::numeric_limits<double>::max();
125-
xMax = -std::numeric_limits<double>::max();
126-
yMin = std::numeric_limits<double>::max();
127-
yMax = -std::numeric_limits<double>::max();
148+
QgsRectangle bBox = g.boundingBox();
128149

129-
Q_FOREACH ( const QgsPointXY &point, pointList )
130-
{
131-
xMin = qMin( xMin, point.x() );
132-
xMax = qMax( xMax, point.x() );
133-
yMin = qMin( yMin, point.y() );
134-
yMax = qMax( yMax, point.y() );
135-
}
150+
QgsExpressionContext context = vlayer->createExpressionContext();
151+
152+
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) );
153+
154+
QgsFeature f;
155+
if ( fit.nextFeature( f ) )
156+
{
157+
//create QgsFeature with wkb representation
158+
QgsFeature ft = QgsVectorLayerUtils::createFeature( vlayer, g, f.attributes().toMap(), &context );
159+
160+
bool res = false;
161+
if ( QApplication::keyboardModifiers() == Qt::ControlModifier )
162+
{
163+
res = vlayer->addFeature( ft );
164+
}
165+
else
166+
{
167+
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, &ft, false, nullptr, true );
168+
dialog->setMode( QgsAttributeForm::AddFeatureMode );
169+
res = dialog->exec(); // will also add the feature
170+
}
136171

137-
bBox.setXMinimum( xMin );
138-
bBox.setYMinimum( yMin );
139-
bBox.setXMaximum( xMax );
140-
bBox.setYMaximum( yMax );
172+
if ( res )
173+
{
174+
vlayer->endEditCommand();
175+
}
176+
else
177+
{
178+
vlayer->destroyEditCommand();
179+
}
180+
}
141181

142-
QgsExpressionContext context = vlayer->createExpressionContext();
182+
if ( isCapturing() )
183+
stopCapturing();
184+
}
143185

144-
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( modifiedFid ) );
186+
// TODO refactor - shamelessly copied from QgsMapToolDeleteRing::ringUnderPoint
187+
QgsGeometry QgsMapToolFillRing::ringUnderPoint( const QgsPointXY &p, QgsFeatureId &fid )
188+
{
189+
//check if we operate on a vector layer
190+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
145191

146-
QgsFeature f;
147-
if ( fit.nextFeature( f ) )
148-
{
149-
QgsGeometry g = QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() );
192+
//There is no clean way to find if we are inside the ring of a feature,
193+
//so we iterate over all the features visible in the canvas
194+
//If several rings are found at this position, the smallest one is chosen,
195+
//in order to be able to delete a ring inside another ring
196+
double area = std::numeric_limits<double>::max();
150197

151-
//create QgsFeature with wkb representation
152-
QgsFeature ft = QgsVectorLayerUtils::createFeature( vlayer, g, f.attributes().toMap(), &context );
198+
QgsGeometry ringGeom;
199+
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( toLayerCoordinates( vlayer, mCanvas->extent() ) ) );
153200

154-
bool res = false;
155-
if ( QApplication::keyboardModifiers() == Qt::ControlModifier )
156-
{
157-
res = vlayer->addFeature( ft );
158-
}
159-
else
160-
{
161-
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, &ft, false, nullptr, true );
162-
dialog->setMode( QgsAttributeForm::AddFeatureMode );
163-
res = dialog->exec(); // will also add the feature
164-
}
201+
QgsFeature f;
202+
while ( fit.nextFeature( f ) )
203+
{
204+
QgsGeometry g = f.geometry();
205+
if ( g.isNull() )
206+
continue;
165207

166-
if ( res )
167-
{
168-
vlayer->endEditCommand();
169-
}
170-
else
208+
QgsMultiPolygon pol;
209+
if ( g.wkbType() == QgsWkbTypes::Polygon || g.wkbType() == QgsWkbTypes::Polygon25D )
210+
{
211+
pol = QgsMultiPolygon() << g.asPolygon();
212+
}
213+
else
214+
{
215+
pol = g.asMultiPolygon();
216+
}
217+
218+
for ( int i = 0; i < pol.size() ; ++i )
219+
{
220+
//for each part
221+
if ( pol[i].size() > 1 )
222+
{
223+
for ( int j = 1; j < pol[i].size(); ++j )
171224
{
172-
vlayer->destroyEditCommand();
225+
QgsPolygon tempPol = QgsPolygon() << pol[i][j];
226+
QgsGeometry tempGeom = QgsGeometry::fromPolygon( tempPol );
227+
if ( tempGeom.area() < area && tempGeom.contains( &p ) )
228+
{
229+
fid = f.id();
230+
area = tempGeom.area();
231+
ringGeom = tempGeom;
232+
}
173233
}
174234
}
175235
}
176-
stopCapturing();
177236
}
237+
return ringGeom;
178238
}

‎src/app/qgsmaptoolfillring.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,11 @@ class APP_EXPORT QgsMapToolFillRing: public QgsMapToolCapture
2727
QgsMapToolFillRing( QgsMapCanvas *canvas );
2828
virtual ~QgsMapToolFillRing();
2929
void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;
30+
31+
private:
32+
33+
/** Return the geometry of the ring under the point p and sets fid to the feature id
34+
*/
35+
QgsGeometry ringUnderPoint( const QgsPointXY &p, QgsFeatureId &fid );
36+
3037
};

0 commit comments

Comments
 (0)
Please sign in to comment.