Skip to content

Commit 3b3d304

Browse files
committedDec 20, 2017
redigitize on feature duplication
there are two actions duplication and duplication redigitize. in redigitize you can set a new geometry for the duplication.
1 parent e1b6daa commit 3b3d304

File tree

7 files changed

+485
-290
lines changed

7 files changed

+485
-290
lines changed
 

‎src/app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ SET(QGIS_APP_SRCS
6969
qgswelcomepage.cpp
7070

7171
qgsmaptooladdfeature.cpp
72+
qgsmaptooldigitizefeature.cpp
7273
qgsmaptooladdpart.cpp
7374
qgsmaptooladdring.cpp
7475
qgsmaptoolfillring.cpp
@@ -294,6 +295,7 @@ SET (QGIS_APP_MOC_HDRS
294295
qgswelcomepage.h
295296

296297
qgsmaptooladdfeature.h
298+
qgsmaptooldigitizefeature.h
297299
qgsmaptoolannotation.h
298300
qgsmaptoolcircularstringradius.h
299301
qgsmaptooladdpart.h

‎src/app/qgisapp.cpp

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
343343
//
344344
// Map tools
345345
//
346-
#include "qgsmaptooladdfeature.h"
347346
#include "qgsmaptooladdpart.h"
347+
#include "qgsmaptooladdfeature.h"
348348
#include "qgsmaptooladdring.h"
349349
#include "qgsmaptoolfillring.h"
350350
#include "qgsmaptoolannotation.h"
@@ -1382,6 +1382,7 @@ QgisApp::~QgisApp()
13821382
delete mMapTools.mRegularPolygonCenterPoint;
13831383
delete mMapTools.mRegularPolygonCenterCorner;
13841384
delete mMapTools.mAddFeature;
1385+
delete mMapTools.mDigitizeFeature;
13851386
delete mpMaptip;
13861387

13871388
delete mpGpsWidget;
@@ -3298,6 +3299,7 @@ void QgisApp::createCanvasTools()
32983299
mMapTools.mAnnotation->setAction( mActionAnnotation );
32993300
mMapTools.mAddFeature = new QgsMapToolAddFeature( mMapCanvas, QgsMapToolCapture::CaptureNone );
33003301
mMapTools.mAddFeature->setAction( mActionAddFeature );
3302+
mMapTools.mDigitizeFeature = new QgsMapToolDigitizeFeature( mMapCanvas, QgsMapToolCapture::CaptureNone );
33013303
mMapTools.mCircularStringCurvePoint = new QgsMapToolCircularStringCurvePoint( mMapTools.mAddFeature, mMapCanvas );
33023304
mMapTools.mCircularStringCurvePoint->setAction( mActionCircularStringCurvePoint );
33033305
mMapTools.mCircularStringRadius = new QgsMapToolCircularStringRadius( mMapTools.mAddFeature, mMapCanvas );
@@ -7621,6 +7623,17 @@ void QgisApp::setupDuplicateFeaturesAction()
76217623
duplicateFeatures( layer, feat );
76227624
}
76237625
);
7626+
7627+
action = new QgsMapLayerAction( QString( tr( "Duplicate feature redigitized" ) ),
7628+
this, QgsMapLayerAction::AllActions,
7629+
QgsApplication::getThemeIcon( QStringLiteral( "/mIconAms.svg" ) ) );
7630+
7631+
QgsGui::mapLayerActionRegistry()->addMapLayerAction( action );
7632+
connect( action, &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
7633+
{
7634+
duplicateFeatureDigitized( layer, feat );
7635+
}
7636+
);
76247637
}
76257638

76267639
void QgisApp::setupAtlasMapLayerAction( QgsComposition *composition, bool enableAction )
@@ -13322,31 +13335,88 @@ QgsFeature QgisApp::duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &fe
1332213335
{
1332313336
if ( mlayer->type() != QgsMapLayer::VectorLayer )
1332413337
return QgsFeature();
13325-
/*
13326-
QgsVectorLayer *layer=qobject_cast<QgsVectorLayer *>(mlayer);
1332713338

13328-
layer->startEditing();
13339+
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );
1332913340

13330-
QgsFeatureList featureList;
13341+
layer->startEditing();
13342+
13343+
QgsFeatureList featureList;
1333113344

13332-
if( feature )
13345+
if ( feature.isValid() )
13346+
{
13347+
featureList.append( feature );
13348+
}
13349+
else
13350+
{
13351+
for ( const QgsFeature &f : layer->selectedFeatures() )
1333313352
{
13334-
featureList.append( feature );
13353+
featureList.append( f );
1333513354
}
13336-
else
13355+
}
13356+
13357+
int featureCount = 0;
13358+
13359+
QString childrenInfo;
13360+
13361+
for ( const QgsFeature &f : featureList )
13362+
{
13363+
QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicateFeatureContext;
13364+
13365+
QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), 0, duplicateFeatureContext );
13366+
featureCount += 1;
13367+
13368+
for ( QgsVectorLayer *chl : duplicateFeatureContext.layers() )
1333713369
{
13338-
for ( const QgsFeature &f : layer->selectedFeatures() )
13339-
{
13340-
featureList.append( f );
13341-
}
13370+
childrenInfo += ( tr( "%1 children on layer %2 duplicated" ).arg( duplicateFeatureContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
1334213371
}
13372+
}
13373+
13374+
messageBar()->pushMessage( tr( "%1 features on layer %2 duplicated\n%3" ).arg( featureCount ).arg( layer->name() ).arg( childrenInfo ), QgsMessageBar::SUCCESS, 5 );
13375+
13376+
return QgsFeature();
13377+
}
13378+
13379+
13380+
QgsFeature QgisApp::duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFeature &feature )
13381+
{
13382+
if ( mlayer->type() != QgsMapLayer::VectorLayer )
13383+
return QgsFeature();
13384+
13385+
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );
1334313386

13344-
int featureCount=0;
13387+
layer->startEditing();
13388+
mMapCanvas->setMapTool( mMapTools.mDigitizeFeature );
13389+
mMapCanvas->window()->raise();
13390+
mMapCanvas->activateWindow();
13391+
mMapCanvas->setFocus();
13392+
13393+
QString msg = tr( "Digitize the duplicate, please." ).arg( layer->name() );
13394+
messageBar()->pushMessage( msg, QgsMessageBar::INFO, 3 );
13395+
13396+
QMetaObject::Connection *connDigitizingFinished = new QMetaObject::Connection();
13397+
*connDigitizingFinished = connect( mMapTools.mDigitizeFeature, static_cast<void ( QgsMapToolDigitizeFeature::* )( const QgsFeature & )>( &QgsMapToolDigitizeFeature::digitizingFinished ), this, [this, layer, feature, connDigitizingFinished]( const QgsFeature & digitizedFeature )
13398+
{
13399+
QString msg = tr( "Duplicate digitized" );
13400+
messageBar()->pushMessage( msg, QgsMessageBar::INFO, 1 );
13401+
13402+
QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicateFeatureContext;
1334513403

13346-
for ( const QgsFeature &f : featureList )
13404+
QgsFeature newFeature = feature;
13405+
newFeature.setGeometry( digitizedFeature.geometry() );
13406+
QgsVectorLayerUtils::duplicateFeature( layer, newFeature, QgsProject::instance(), 0, duplicateFeatureContext );
13407+
13408+
QString childrenInfo;
13409+
for ( QgsVectorLayer *chl : duplicateFeatureContext.layers() )
1334713410
{
13348-
//QgsVectorLayerUtils::duplicateFeature( layer, feature, QgsProject::instance(), 0 );
13411+
childrenInfo += ( tr( "%1 children on layer %2 duplicated" ).arg( duplicateFeatureContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
1334913412
}
13350-
*/
13413+
13414+
messageBar()->pushMessage( tr( "Feature on layer %2 duplicated\n%3" ).arg( layer->name() ).arg( childrenInfo ), QgsMessageBar::SUCCESS, 5 );
13415+
mMapCanvas->unsetMapTool( mMapTools.mDigitizeFeature );
13416+
disconnect( *connDigitizingFinished );
13417+
}
13418+
);
13419+
1335113420
return QgsFeature();
1335213421
}
13422+

‎src/app/qgisapp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class QgsMapOverviewCanvas;
7474
class QgsMapTip;
7575
class QgsMapTool;
7676
class QgsMapToolAddFeature;
77+
class QgsMapToolDigitizeFeature;
7778
class QgsMapToolAdvancedDigitizing;
7879
class QgsMapToolIdentifyAction;
7980
class QgsPluginLayer;
@@ -1940,6 +1941,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
19401941
QgsMapTool *mMeasureArea = nullptr;
19411942
QgsMapTool *mMeasureAngle = nullptr;
19421943
QgsMapToolAddFeature *mAddFeature = nullptr;
1944+
QgsMapToolDigitizeFeature *mDigitizeFeature = nullptr;
19431945
QgsMapTool *mCircularStringCurvePoint = nullptr;
19441946
QgsMapTool *mCircularStringRadius = nullptr;
19451947
QgsMapTool *mCircle2Points = nullptr;
@@ -2209,7 +2211,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
22092211
QgsBrowserModel *mBrowserModel = nullptr;
22102212

22112213
void setupDuplicateFeaturesAction();
2214+
22122215
QgsFeature duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &feature );
2216+
QgsFeature duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFeature &feature );
22132217

22142218
friend class TestQgisAppPython;
22152219
};

‎src/app/qgsmaptooladdfeature.cpp

Lines changed: 20 additions & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#include <QSettings>
3838

3939
QgsMapToolAddFeature::QgsMapToolAddFeature( QgsMapCanvas *canvas, CaptureMode mode )
40-
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), mode )
40+
: QgsMapToolDigitizeFeature( canvas, mode )
4141
, mCheckGeometryType( true )
4242
{
4343
mToolName = tr( "Add feature" );
@@ -55,282 +55,34 @@ bool QgsMapToolAddFeature::addFeature( QgsVectorLayer *vlayer, QgsFeature *f, bo
5555
return res;
5656
}
5757

58-
void QgsMapToolAddFeature::activate()
59-
{
60-
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
61-
if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
62-
{
63-
QgsFeature f;
64-
addFeature( vlayer, &f, false );
65-
return;
66-
}
67-
68-
QgsMapToolCapture::activate();
69-
}
70-
71-
bool QgsMapToolAddFeature::checkGeometryType() const
72-
{
73-
return mCheckGeometryType;
74-
}
75-
76-
void QgsMapToolAddFeature::setCheckGeometryType( bool checkGeometryType )
77-
{
78-
mCheckGeometryType = checkGeometryType;
79-
}
80-
81-
void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
58+
void QgsMapToolAddFeature::digitized( QgsFeature *f )
8259
{
8360
QgsVectorLayer *vlayer = currentVectorLayer();
61+
bool res = addFeature( vlayer, f, false );
8462

85-
if ( !vlayer )
86-
{
87-
notifyNotVectorLayer();
88-
return;
89-
}
90-
91-
QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
92-
93-
QgsVectorDataProvider *provider = vlayer->dataProvider();
94-
95-
if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
96-
{
97-
emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING );
98-
return;
99-
}
100-
101-
if ( !vlayer->isEditable() )
102-
{
103-
notifyNotEditableLayer();
104-
return;
105-
}
106-
107-
// POINT CAPTURING
108-
if ( mode() == CapturePoint )
109-
{
110-
if ( e->button() != Qt::LeftButton )
111-
return;
112-
113-
//check we only use this tool for point/multipoint layers
114-
if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
115-
{
116-
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING );
117-
return;
118-
}
119-
120-
121-
122-
QgsPointXY savePoint; //point in layer coordinates
123-
try
124-
{
125-
QgsPoint fetchPoint;
126-
int res;
127-
res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
128-
if ( res == 0 )
129-
{
130-
savePoint = QgsPointXY( fetchPoint.x(), fetchPoint.y() );
131-
}
132-
else
133-
{
134-
savePoint = toLayerCoordinates( vlayer, e->mapPoint() );
135-
}
136-
QgsDebugMsg( "savePoint = " + savePoint.toString() );
137-
}
138-
catch ( QgsCsException &cse )
139-
{
140-
Q_UNUSED( cse );
141-
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
142-
return;
143-
}
144-
145-
//only do the rest for provider with feature addition support
146-
//note that for the grass provider, this will return false since
147-
//grass provider has its own mechanism of feature addition
148-
if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures )
149-
{
150-
QgsFeature f( vlayer->fields(), 0 );
151-
152-
QgsGeometry g;
153-
if ( layerWKBType == QgsWkbTypes::Point )
154-
{
155-
g = QgsGeometry::fromPointXY( savePoint );
156-
}
157-
else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
158-
{
159-
g = QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
160-
}
161-
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
162-
{
163-
g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
164-
}
165-
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
166-
{
167-
QgsMultiPoint *mp = new QgsMultiPoint();
168-
mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
169-
g = QgsGeometry( mp );
170-
}
171-
else
172-
{
173-
// if layer supports more types (mCheckGeometryType is false)
174-
g = QgsGeometry::fromPointXY( savePoint );
175-
}
176-
177-
if ( QgsWkbTypes::hasM( layerWKBType ) )
178-
{
179-
g.get()->addMValue();
180-
}
181-
182-
f.setGeometry( g );
183-
f.setValid( true );
184-
185-
addFeature( vlayer, &f, false );
186-
187-
// we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
188-
cadDockWidget()->clearPoints();
189-
}
190-
}
191-
192-
// LINE AND POLYGON CAPTURING
193-
else if ( mode() == CaptureLine || mode() == CapturePolygon )
63+
if ( res && ( mode() == CaptureLine || mode() == CapturePolygon ) )
19464
{
195-
//check we only use the line tool for line/multiline layers
196-
if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
197-
{
198-
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING );
199-
return;
200-
}
201-
202-
//check we only use the polygon tool for polygon/multipolygon layers
203-
if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
204-
{
205-
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING );
206-
return;
207-
}
208-
209-
//add point to list and to rubber band
210-
if ( e->button() == Qt::LeftButton )
211-
{
212-
int error = addVertex( e->mapPoint(), e->mapPointMatch() );
213-
if ( error == 1 )
214-
{
215-
//current layer is not a vector layer
216-
return;
217-
}
218-
else if ( error == 2 )
219-
{
220-
//problem with coordinate transformation
221-
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
222-
return;
223-
}
224-
225-
startCapturing();
226-
}
227-
else if ( e->button() == Qt::RightButton )
65+
//add points to other features to keep topology up-to-date
66+
bool topologicalEditing = QgsProject::instance()->topologicalEditing();
67+
68+
//use always topological editing for avoidIntersection.
69+
//Otherwise, no way to guarantee the geometries don't have a small gap in between.
70+
QList<QgsVectorLayer *> intersectionLayers = QgsProject::instance()->avoidIntersectionsLayers();
71+
bool avoidIntersection = !intersectionLayers.isEmpty();
72+
if ( avoidIntersection ) //try to add topological points also to background layers
22873
{
229-
// End of string
230-
deleteTempRubberBand();
231-
232-
//lines: bail out if there are not at least two vertices
233-
if ( mode() == CaptureLine && size() < 2 )
234-
{
235-
stopCapturing();
236-
return;
237-
}
238-
239-
//polygons: bail out if there are not at least two vertices
240-
if ( mode() == CapturePolygon && size() < 3 )
241-
{
242-
stopCapturing();
243-
return;
244-
}
245-
246-
if ( mode() == CapturePolygon )
74+
Q_FOREACH ( QgsVectorLayer *vl, intersectionLayers )
24775
{
248-
closePolygon();
249-
}
250-
251-
//create QgsFeature with wkb representation
252-
std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
253-
254-
//does compoundcurve contain circular strings?
255-
//does provider support circular strings?
256-
bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
257-
bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
258-
259-
QList<QgsPointLocator::Match> snappingMatchesList;
260-
QgsCurve *curveToAdd = nullptr;
261-
if ( hasCurvedSegments && providerSupportsCurvedSegments )
262-
{
263-
curveToAdd = captureCurve()->clone();
264-
}
265-
else
266-
{
267-
curveToAdd = captureCurve()->curveToLine();
268-
snappingMatchesList = snappingMatches();
269-
}
270-
271-
if ( mode() == CaptureLine )
272-
{
273-
QgsGeometry g( curveToAdd );
274-
f->setGeometry( g );
275-
}
276-
else
277-
{
278-
QgsCurvePolygon *poly = nullptr;
279-
if ( hasCurvedSegments && providerSupportsCurvedSegments )
280-
{
281-
poly = new QgsCurvePolygon();
282-
}
283-
else
284-
{
285-
poly = new QgsPolygon();
286-
}
287-
poly->setExteriorRing( curveToAdd );
288-
QgsGeometry g( poly );
289-
f->setGeometry( g );
290-
291-
QgsGeometry featGeom = f->geometry();
292-
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
293-
f->setGeometry( featGeom );
294-
if ( avoidIntersectionsReturn == 1 )
76+
//can only add topological points if background layer is editable...
77+
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
29578
{
296-
//not a polygon type. Impossible to get there
297-
}
298-
if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
299-
{
300-
emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), QgsMessageBar::CRITICAL );
301-
stopCapturing();
302-
return;
79+
vl->addTopologicalPoints( f->geometry() );
30380
}
30481
}
305-
f->setValid( true );
306-
307-
if ( addFeature( vlayer, f.get(), false ) )
308-
{
309-
//add points to other features to keep topology up-to-date
310-
bool topologicalEditing = QgsProject::instance()->topologicalEditing();
311-
312-
//use always topological editing for avoidIntersection.
313-
//Otherwise, no way to guarantee the geometries don't have a small gap in between.
314-
QList<QgsVectorLayer *> intersectionLayers = QgsProject::instance()->avoidIntersectionsLayers();
315-
bool avoidIntersection = !intersectionLayers.isEmpty();
316-
if ( avoidIntersection ) //try to add topological points also to background layers
317-
{
318-
Q_FOREACH ( QgsVectorLayer *vl, intersectionLayers )
319-
{
320-
//can only add topological points if background layer is editable...
321-
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
322-
{
323-
vl->addTopologicalPoints( f->geometry() );
324-
}
325-
}
326-
}
327-
else if ( topologicalEditing )
328-
{
329-
vlayer->addTopologicalPoints( f->geometry() );
330-
}
331-
}
332-
333-
stopCapturing();
82+
}
83+
else if ( topologicalEditing )
84+
{
85+
vlayer->addTopologicalPoints( f->geometry() );
33486
}
33587
}
33688
}

‎src/app/qgsmaptooladdfeature.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,19 @@
1313
* *
1414
***************************************************************************/
1515

16-
#include "qgsmaptoolcapture.h"
17-
#include "qgis_app.h"
16+
#include "qgsmaptooldigitizefeature.h"
1817

1918
//! This tool adds new point/line/polygon features to already existing vector layers
20-
class APP_EXPORT QgsMapToolAddFeature : public QgsMapToolCapture
19+
class APP_EXPORT QgsMapToolAddFeature : public QgsMapToolDigitizeFeature
2120
{
2221
Q_OBJECT
2322
public:
2423
//! \since QGIS 2.12
2524
QgsMapToolAddFeature( QgsMapCanvas *canvas, CaptureMode mode );
2625

27-
void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;
28-
2926
bool addFeature( QgsVectorLayer *vlayer, QgsFeature *f, bool showModal = true );
30-
void activate() override;
27+
28+
void digitized( QgsFeature *f ) override;
3129

3230
protected:
3331

‎src/app/qgsmaptooldigitizefeature.cpp

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/***************************************************************************
2+
qgsmaptooldigitizefeature- %{Cpp:License:ClassName}
3+
4+
---------------------
5+
begin : 7.12.2017
6+
copyright : (C) 2017 by david
7+
email : [your-email-here]
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 "qgsmaptooldigitizefeature.h"
18+
#include "qgsadvanceddigitizingdockwidget.h"
19+
#include "qgsapplication.h"
20+
#include "qgsattributedialog.h"
21+
#include "qgsexception.h"
22+
#include "qgscurvepolygon.h"
23+
#include "qgsfields.h"
24+
#include "qgsgeometry.h"
25+
#include "qgslinestring.h"
26+
#include "qgsmultipoint.h"
27+
#include "qgsmapcanvas.h"
28+
#include "qgsmapmouseevent.h"
29+
#include "qgspolygon.h"
30+
#include "qgsproject.h"
31+
#include "qgsvectordataprovider.h"
32+
#include "qgsvectorlayer.h"
33+
#include "qgslogger.h"
34+
#include "qgsfeatureaction.h"
35+
#include "qgisapp.h"
36+
37+
#include <QMouseEvent>
38+
#include <QSettings>
39+
40+
QgsMapToolDigitizeFeature::QgsMapToolDigitizeFeature( QgsMapCanvas *canvas, CaptureMode mode )
41+
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), mode )
42+
, mCheckGeometryType( true )
43+
{
44+
mToolName = tr( "Add feature" );
45+
connect( QgisApp::instance(), &QgisApp::newProject, this, &QgsMapToolDigitizeFeature::stopCapturing );
46+
connect( QgisApp::instance(), &QgisApp::projectRead, this, &QgsMapToolDigitizeFeature::stopCapturing );
47+
}
48+
49+
void QgsMapToolDigitizeFeature::digitized( QgsFeature *f )
50+
{
51+
emit digitizingFinished( static_cast< const QgsFeature & > ( *f ) );
52+
}
53+
54+
void QgsMapToolDigitizeFeature::activate()
55+
{
56+
QgsVectorLayer *vlayer = currentVectorLayer();
57+
if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
58+
{
59+
QgsFeature f;
60+
digitized( &f );
61+
return;
62+
}
63+
64+
QgsMapToolCapture::activate();
65+
}
66+
67+
bool QgsMapToolDigitizeFeature::checkGeometryType() const
68+
{
69+
return mCheckGeometryType;
70+
}
71+
72+
void QgsMapToolDigitizeFeature::setCheckGeometryType( bool checkGeometryType )
73+
{
74+
mCheckGeometryType = checkGeometryType;
75+
}
76+
77+
void QgsMapToolDigitizeFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
78+
{
79+
QgsVectorLayer *vlayer = currentVectorLayer();
80+
81+
if ( !vlayer )
82+
{
83+
notifyNotVectorLayer();
84+
return;
85+
}
86+
87+
QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
88+
89+
QgsVectorDataProvider *provider = vlayer->dataProvider();
90+
91+
if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
92+
{
93+
emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING );
94+
return;
95+
}
96+
97+
if ( !vlayer->isEditable() )
98+
{
99+
notifyNotEditableLayer();
100+
return;
101+
}
102+
103+
// POINT CAPTURING
104+
if ( mode() == CapturePoint )
105+
{
106+
if ( e->button() != Qt::LeftButton )
107+
return;
108+
109+
//check we only use this tool for point/multipoint layers
110+
if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
111+
{
112+
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING );
113+
return;
114+
}
115+
116+
117+
118+
QgsPointXY savePoint; //point in layer coordinates
119+
try
120+
{
121+
QgsPoint fetchPoint;
122+
int res;
123+
res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
124+
if ( res == 0 )
125+
{
126+
savePoint = QgsPointXY( fetchPoint.x(), fetchPoint.y() );
127+
}
128+
else
129+
{
130+
savePoint = toLayerCoordinates( vlayer, e->mapPoint() );
131+
}
132+
QgsDebugMsg( "savePoint = " + savePoint.toString() );
133+
}
134+
catch ( QgsCsException &cse )
135+
{
136+
Q_UNUSED( cse );
137+
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
138+
return;
139+
}
140+
141+
//only do the rest for provider with feature addition support
142+
//note that for the grass provider, this will return false since
143+
//grass provider has its own mechanism of feature addition
144+
if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures )
145+
{
146+
QgsFeature f( vlayer->fields(), 0 );
147+
148+
QgsGeometry g;
149+
if ( layerWKBType == QgsWkbTypes::Point )
150+
{
151+
g = QgsGeometry::fromPointXY( savePoint );
152+
}
153+
else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
154+
{
155+
g = QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
156+
}
157+
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
158+
{
159+
g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
160+
}
161+
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
162+
{
163+
QgsMultiPoint *mp = new QgsMultiPoint();
164+
mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
165+
g = QgsGeometry( mp );
166+
}
167+
else
168+
{
169+
// if layer supports more types (mCheckGeometryType is false)
170+
g = QgsGeometry::fromPointXY( savePoint );
171+
}
172+
173+
if ( QgsWkbTypes::hasM( layerWKBType ) )
174+
{
175+
g.get()->addMValue();
176+
}
177+
178+
f.setGeometry( g );
179+
f.setValid( true );
180+
181+
digitized( &f );
182+
183+
// we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
184+
cadDockWidget()->clearPoints();
185+
}
186+
}
187+
188+
// LINE AND POLYGON CAPTURING
189+
else if ( mode() == CaptureLine || mode() == CapturePolygon )
190+
{
191+
//check we only use the line tool for line/multiline layers
192+
if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
193+
{
194+
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING );
195+
return;
196+
}
197+
198+
//check we only use the polygon tool for polygon/multipolygon layers
199+
if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
200+
{
201+
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING );
202+
return;
203+
}
204+
205+
//add point to list and to rubber band
206+
if ( e->button() == Qt::LeftButton )
207+
{
208+
int error = addVertex( e->mapPoint(), e->mapPointMatch() );
209+
if ( error == 1 )
210+
{
211+
//current layer is not a vector layer
212+
return;
213+
}
214+
else if ( error == 2 )
215+
{
216+
//problem with coordinate transformation
217+
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
218+
return;
219+
}
220+
221+
startCapturing();
222+
}
223+
else if ( e->button() == Qt::RightButton )
224+
{
225+
// End of string
226+
deleteTempRubberBand();
227+
228+
//lines: bail out if there are not at least two vertices
229+
if ( mode() == CaptureLine && size() < 2 )
230+
{
231+
stopCapturing();
232+
return;
233+
}
234+
235+
//polygons: bail out if there are not at least two vertices
236+
if ( mode() == CapturePolygon && size() < 3 )
237+
{
238+
stopCapturing();
239+
return;
240+
}
241+
242+
if ( mode() == CapturePolygon )
243+
{
244+
closePolygon();
245+
}
246+
247+
//create QgsFeature with wkb representation
248+
std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
249+
250+
//does compoundcurve contain circular strings?
251+
//does provider support circular strings?
252+
bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
253+
bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
254+
255+
QList<QgsPointLocator::Match> snappingMatchesList;
256+
QgsCurve *curveToAdd = nullptr;
257+
if ( hasCurvedSegments && providerSupportsCurvedSegments )
258+
{
259+
curveToAdd = captureCurve()->clone();
260+
}
261+
else
262+
{
263+
curveToAdd = captureCurve()->curveToLine();
264+
snappingMatchesList = snappingMatches();
265+
}
266+
267+
if ( mode() == CaptureLine )
268+
{
269+
QgsGeometry g( curveToAdd );
270+
f->setGeometry( g );
271+
}
272+
else
273+
{
274+
QgsCurvePolygon *poly = nullptr;
275+
if ( hasCurvedSegments && providerSupportsCurvedSegments )
276+
{
277+
poly = new QgsCurvePolygon();
278+
}
279+
else
280+
{
281+
poly = new QgsPolygon();
282+
}
283+
poly->setExteriorRing( curveToAdd );
284+
QgsGeometry g( poly );
285+
f->setGeometry( g );
286+
287+
QgsGeometry featGeom = f->geometry();
288+
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
289+
f->setGeometry( featGeom );
290+
if ( avoidIntersectionsReturn == 1 )
291+
{
292+
//not a polygon type. Impossible to get there
293+
}
294+
if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
295+
{
296+
emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), QgsMessageBar::CRITICAL );
297+
stopCapturing();
298+
return;
299+
}
300+
}
301+
f->setValid( true );
302+
303+
digitized( f.get() );
304+
305+
stopCapturing();
306+
}
307+
}
308+
}

‎src/app/qgsmaptooldigitizefeature.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/***************************************************************************
2+
qgsmaptooldigitizegeometry - %{Cpp:License:ClassName}
3+
4+
---------------------
5+
begin : 7.12.2017
6+
copyright : (C) 2017 by david
7+
email : [your-email-here]
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+
#ifndef QGSMAPTOOLDIGITIZEFEATURE_H
17+
#define QGSMAPTOOLDIGITIZEFEATURE_H
18+
19+
#include "qgsmaptoolcapture.h"
20+
#include "qgis_app.h"
21+
22+
//! This tool digitizes geometry of new point/line/polygon features on already existing vector layers
23+
class APP_EXPORT QgsMapToolDigitizeFeature : public QgsMapToolCapture
24+
{
25+
Q_OBJECT
26+
public:
27+
//! \since QGIS 2.12
28+
QgsMapToolDigitizeFeature( QgsMapCanvas *canvas, CaptureMode mode );
29+
30+
void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;
31+
32+
virtual void digitized( QgsFeature *f );
33+
34+
virtual void activate() override;
35+
36+
signals:
37+
void digitizingFinished( const QgsFeature & );
38+
39+
protected:
40+
41+
/**
42+
* Check if CaptureMode matches layer type. Default is true.
43+
* \since QGIS 3.0
44+
*/
45+
bool checkGeometryType() const;
46+
47+
/**
48+
* Check if CaptureMode matches layer type. Default is true.
49+
* \since QGIS 3.0
50+
*/
51+
void setCheckGeometryType( bool checkGeometryType );
52+
53+
private:
54+
55+
/**
56+
* Check if CaptureMode matches layer type. Default is true.
57+
* \since QGIS 2.12 */
58+
bool mCheckGeometryType;
59+
};
60+
61+
#endif // QGSMAPTOOLDIGITIZEFEATURE_H

0 commit comments

Comments
 (0)
Please sign in to comment.