mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-10 00:05:25 -04:00
redigitize on feature duplication
there are two actions duplication and duplication redigitize. in redigitize you can set a new geometry for the duplication.
This commit is contained in:
parent
e1b6daa474
commit
3b3d3048e3
@ -69,6 +69,7 @@ SET(QGIS_APP_SRCS
|
||||
qgswelcomepage.cpp
|
||||
|
||||
qgsmaptooladdfeature.cpp
|
||||
qgsmaptooldigitizefeature.cpp
|
||||
qgsmaptooladdpart.cpp
|
||||
qgsmaptooladdring.cpp
|
||||
qgsmaptoolfillring.cpp
|
||||
@ -294,6 +295,7 @@ SET (QGIS_APP_MOC_HDRS
|
||||
qgswelcomepage.h
|
||||
|
||||
qgsmaptooladdfeature.h
|
||||
qgsmaptooldigitizefeature.h
|
||||
qgsmaptoolannotation.h
|
||||
qgsmaptoolcircularstringradius.h
|
||||
qgsmaptooladdpart.h
|
||||
|
@ -343,8 +343,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
|
||||
//
|
||||
// Map tools
|
||||
//
|
||||
#include "qgsmaptooladdfeature.h"
|
||||
#include "qgsmaptooladdpart.h"
|
||||
#include "qgsmaptooladdfeature.h"
|
||||
#include "qgsmaptooladdring.h"
|
||||
#include "qgsmaptoolfillring.h"
|
||||
#include "qgsmaptoolannotation.h"
|
||||
@ -1382,6 +1382,7 @@ QgisApp::~QgisApp()
|
||||
delete mMapTools.mRegularPolygonCenterPoint;
|
||||
delete mMapTools.mRegularPolygonCenterCorner;
|
||||
delete mMapTools.mAddFeature;
|
||||
delete mMapTools.mDigitizeFeature;
|
||||
delete mpMaptip;
|
||||
|
||||
delete mpGpsWidget;
|
||||
@ -3298,6 +3299,7 @@ void QgisApp::createCanvasTools()
|
||||
mMapTools.mAnnotation->setAction( mActionAnnotation );
|
||||
mMapTools.mAddFeature = new QgsMapToolAddFeature( mMapCanvas, QgsMapToolCapture::CaptureNone );
|
||||
mMapTools.mAddFeature->setAction( mActionAddFeature );
|
||||
mMapTools.mDigitizeFeature = new QgsMapToolDigitizeFeature( mMapCanvas, QgsMapToolCapture::CaptureNone );
|
||||
mMapTools.mCircularStringCurvePoint = new QgsMapToolCircularStringCurvePoint( mMapTools.mAddFeature, mMapCanvas );
|
||||
mMapTools.mCircularStringCurvePoint->setAction( mActionCircularStringCurvePoint );
|
||||
mMapTools.mCircularStringRadius = new QgsMapToolCircularStringRadius( mMapTools.mAddFeature, mMapCanvas );
|
||||
@ -7621,6 +7623,17 @@ void QgisApp::setupDuplicateFeaturesAction()
|
||||
duplicateFeatures( layer, feat );
|
||||
}
|
||||
);
|
||||
|
||||
action = new QgsMapLayerAction( QString( tr( "Duplicate feature redigitized" ) ),
|
||||
this, QgsMapLayerAction::AllActions,
|
||||
QgsApplication::getThemeIcon( QStringLiteral( "/mIconAms.svg" ) ) );
|
||||
|
||||
QgsGui::mapLayerActionRegistry()->addMapLayerAction( action );
|
||||
connect( action, &QgsMapLayerAction::triggeredForFeature, this, [this]( QgsMapLayer * layer, const QgsFeature & feat )
|
||||
{
|
||||
duplicateFeatureDigitized( layer, feat );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void QgisApp::setupAtlasMapLayerAction( QgsComposition *composition, bool enableAction )
|
||||
@ -13322,31 +13335,88 @@ QgsFeature QgisApp::duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &fe
|
||||
{
|
||||
if ( mlayer->type() != QgsMapLayer::VectorLayer )
|
||||
return QgsFeature();
|
||||
/*
|
||||
QgsVectorLayer *layer=qobject_cast<QgsVectorLayer *>(mlayer);
|
||||
|
||||
layer->startEditing();
|
||||
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );
|
||||
|
||||
QgsFeatureList featureList;
|
||||
layer->startEditing();
|
||||
|
||||
if( feature )
|
||||
QgsFeatureList featureList;
|
||||
|
||||
if ( feature.isValid() )
|
||||
{
|
||||
featureList.append( feature );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const QgsFeature &f : layer->selectedFeatures() )
|
||||
{
|
||||
featureList.append( feature );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( const QgsFeature &f : layer->selectedFeatures() )
|
||||
{
|
||||
featureList.append( f );
|
||||
}
|
||||
featureList.append( f );
|
||||
}
|
||||
}
|
||||
|
||||
int featureCount=0;
|
||||
int featureCount = 0;
|
||||
|
||||
for ( const QgsFeature &f : featureList )
|
||||
QString childrenInfo;
|
||||
|
||||
for ( const QgsFeature &f : featureList )
|
||||
{
|
||||
QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicateFeatureContext;
|
||||
|
||||
QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), 0, duplicateFeatureContext );
|
||||
featureCount += 1;
|
||||
|
||||
for ( QgsVectorLayer *chl : duplicateFeatureContext.layers() )
|
||||
{
|
||||
//QgsVectorLayerUtils::duplicateFeature( layer, feature, QgsProject::instance(), 0 );
|
||||
childrenInfo += ( tr( "%1 children on layer %2 duplicated" ).arg( duplicateFeatureContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
messageBar()->pushMessage( tr( "%1 features on layer %2 duplicated\n%3" ).arg( featureCount ).arg( layer->name() ).arg( childrenInfo ), QgsMessageBar::SUCCESS, 5 );
|
||||
|
||||
return QgsFeature();
|
||||
}
|
||||
|
||||
|
||||
QgsFeature QgisApp::duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFeature &feature )
|
||||
{
|
||||
if ( mlayer->type() != QgsMapLayer::VectorLayer )
|
||||
return QgsFeature();
|
||||
|
||||
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mlayer );
|
||||
|
||||
layer->startEditing();
|
||||
mMapCanvas->setMapTool( mMapTools.mDigitizeFeature );
|
||||
mMapCanvas->window()->raise();
|
||||
mMapCanvas->activateWindow();
|
||||
mMapCanvas->setFocus();
|
||||
|
||||
QString msg = tr( "Digitize the duplicate, please." ).arg( layer->name() );
|
||||
messageBar()->pushMessage( msg, QgsMessageBar::INFO, 3 );
|
||||
|
||||
QMetaObject::Connection *connDigitizingFinished = new QMetaObject::Connection();
|
||||
*connDigitizingFinished = connect( mMapTools.mDigitizeFeature, static_cast<void ( QgsMapToolDigitizeFeature::* )( const QgsFeature & )>( &QgsMapToolDigitizeFeature::digitizingFinished ), this, [this, layer, feature, connDigitizingFinished]( const QgsFeature & digitizedFeature )
|
||||
{
|
||||
QString msg = tr( "Duplicate digitized" );
|
||||
messageBar()->pushMessage( msg, QgsMessageBar::INFO, 1 );
|
||||
|
||||
QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicateFeatureContext;
|
||||
|
||||
QgsFeature newFeature = feature;
|
||||
newFeature.setGeometry( digitizedFeature.geometry() );
|
||||
QgsVectorLayerUtils::duplicateFeature( layer, newFeature, QgsProject::instance(), 0, duplicateFeatureContext );
|
||||
|
||||
QString childrenInfo;
|
||||
for ( QgsVectorLayer *chl : duplicateFeatureContext.layers() )
|
||||
{
|
||||
childrenInfo += ( tr( "%1 children on layer %2 duplicated" ).arg( duplicateFeatureContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
|
||||
}
|
||||
|
||||
messageBar()->pushMessage( tr( "Feature on layer %2 duplicated\n%3" ).arg( layer->name() ).arg( childrenInfo ), QgsMessageBar::SUCCESS, 5 );
|
||||
mMapCanvas->unsetMapTool( mMapTools.mDigitizeFeature );
|
||||
disconnect( *connDigitizingFinished );
|
||||
}
|
||||
);
|
||||
|
||||
return QgsFeature();
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ class QgsMapOverviewCanvas;
|
||||
class QgsMapTip;
|
||||
class QgsMapTool;
|
||||
class QgsMapToolAddFeature;
|
||||
class QgsMapToolDigitizeFeature;
|
||||
class QgsMapToolAdvancedDigitizing;
|
||||
class QgsMapToolIdentifyAction;
|
||||
class QgsPluginLayer;
|
||||
@ -1940,6 +1941,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
QgsMapTool *mMeasureArea = nullptr;
|
||||
QgsMapTool *mMeasureAngle = nullptr;
|
||||
QgsMapToolAddFeature *mAddFeature = nullptr;
|
||||
QgsMapToolDigitizeFeature *mDigitizeFeature = nullptr;
|
||||
QgsMapTool *mCircularStringCurvePoint = nullptr;
|
||||
QgsMapTool *mCircularStringRadius = nullptr;
|
||||
QgsMapTool *mCircle2Points = nullptr;
|
||||
@ -2209,7 +2211,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
QgsBrowserModel *mBrowserModel = nullptr;
|
||||
|
||||
void setupDuplicateFeaturesAction();
|
||||
|
||||
QgsFeature duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &feature );
|
||||
QgsFeature duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFeature &feature );
|
||||
|
||||
friend class TestQgisAppPython;
|
||||
};
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <QSettings>
|
||||
|
||||
QgsMapToolAddFeature::QgsMapToolAddFeature( QgsMapCanvas *canvas, CaptureMode mode )
|
||||
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), mode )
|
||||
: QgsMapToolDigitizeFeature( canvas, mode )
|
||||
, mCheckGeometryType( true )
|
||||
{
|
||||
mToolName = tr( "Add feature" );
|
||||
@ -55,282 +55,34 @@ bool QgsMapToolAddFeature::addFeature( QgsVectorLayer *vlayer, QgsFeature *f, bo
|
||||
return res;
|
||||
}
|
||||
|
||||
void QgsMapToolAddFeature::activate()
|
||||
{
|
||||
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
|
||||
if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
|
||||
{
|
||||
QgsFeature f;
|
||||
addFeature( vlayer, &f, false );
|
||||
return;
|
||||
}
|
||||
|
||||
QgsMapToolCapture::activate();
|
||||
}
|
||||
|
||||
bool QgsMapToolAddFeature::checkGeometryType() const
|
||||
{
|
||||
return mCheckGeometryType;
|
||||
}
|
||||
|
||||
void QgsMapToolAddFeature::setCheckGeometryType( bool checkGeometryType )
|
||||
{
|
||||
mCheckGeometryType = checkGeometryType;
|
||||
}
|
||||
|
||||
void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
|
||||
void QgsMapToolAddFeature::digitized( QgsFeature *f )
|
||||
{
|
||||
QgsVectorLayer *vlayer = currentVectorLayer();
|
||||
bool res = addFeature( vlayer, f, false );
|
||||
|
||||
if ( !vlayer )
|
||||
if ( res && ( mode() == CaptureLine || mode() == CapturePolygon ) )
|
||||
{
|
||||
notifyNotVectorLayer();
|
||||
return;
|
||||
}
|
||||
//add points to other features to keep topology up-to-date
|
||||
bool topologicalEditing = QgsProject::instance()->topologicalEditing();
|
||||
|
||||
QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
|
||||
|
||||
QgsVectorDataProvider *provider = vlayer->dataProvider();
|
||||
|
||||
if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
|
||||
{
|
||||
emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !vlayer->isEditable() )
|
||||
{
|
||||
notifyNotEditableLayer();
|
||||
return;
|
||||
}
|
||||
|
||||
// POINT CAPTURING
|
||||
if ( mode() == CapturePoint )
|
||||
{
|
||||
if ( e->button() != Qt::LeftButton )
|
||||
return;
|
||||
|
||||
//check we only use this tool for point/multipoint layers
|
||||
if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
|
||||
//use always topological editing for avoidIntersection.
|
||||
//Otherwise, no way to guarantee the geometries don't have a small gap in between.
|
||||
QList<QgsVectorLayer *> intersectionLayers = QgsProject::instance()->avoidIntersectionsLayers();
|
||||
bool avoidIntersection = !intersectionLayers.isEmpty();
|
||||
if ( avoidIntersection ) //try to add topological points also to background layers
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QgsPointXY savePoint; //point in layer coordinates
|
||||
try
|
||||
{
|
||||
QgsPoint fetchPoint;
|
||||
int res;
|
||||
res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
|
||||
if ( res == 0 )
|
||||
Q_FOREACH ( QgsVectorLayer *vl, intersectionLayers )
|
||||
{
|
||||
savePoint = QgsPointXY( fetchPoint.x(), fetchPoint.y() );
|
||||
}
|
||||
else
|
||||
{
|
||||
savePoint = toLayerCoordinates( vlayer, e->mapPoint() );
|
||||
}
|
||||
QgsDebugMsg( "savePoint = " + savePoint.toString() );
|
||||
}
|
||||
catch ( QgsCsException &cse )
|
||||
{
|
||||
Q_UNUSED( cse );
|
||||
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//only do the rest for provider with feature addition support
|
||||
//note that for the grass provider, this will return false since
|
||||
//grass provider has its own mechanism of feature addition
|
||||
if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures )
|
||||
{
|
||||
QgsFeature f( vlayer->fields(), 0 );
|
||||
|
||||
QgsGeometry g;
|
||||
if ( layerWKBType == QgsWkbTypes::Point )
|
||||
{
|
||||
g = QgsGeometry::fromPointXY( savePoint );
|
||||
}
|
||||
else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
g = QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
|
||||
}
|
||||
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
|
||||
}
|
||||
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
QgsMultiPoint *mp = new QgsMultiPoint();
|
||||
mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
|
||||
g = QgsGeometry( mp );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if layer supports more types (mCheckGeometryType is false)
|
||||
g = QgsGeometry::fromPointXY( savePoint );
|
||||
}
|
||||
|
||||
if ( QgsWkbTypes::hasM( layerWKBType ) )
|
||||
{
|
||||
g.get()->addMValue();
|
||||
}
|
||||
|
||||
f.setGeometry( g );
|
||||
f.setValid( true );
|
||||
|
||||
addFeature( vlayer, &f, false );
|
||||
|
||||
// we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
|
||||
cadDockWidget()->clearPoints();
|
||||
}
|
||||
}
|
||||
|
||||
// LINE AND POLYGON CAPTURING
|
||||
else if ( mode() == CaptureLine || mode() == CapturePolygon )
|
||||
{
|
||||
//check we only use the line tool for line/multiline layers
|
||||
if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//check we only use the polygon tool for polygon/multipolygon layers
|
||||
if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//add point to list and to rubber band
|
||||
if ( e->button() == Qt::LeftButton )
|
||||
{
|
||||
int error = addVertex( e->mapPoint(), e->mapPointMatch() );
|
||||
if ( error == 1 )
|
||||
{
|
||||
//current layer is not a vector layer
|
||||
return;
|
||||
}
|
||||
else if ( error == 2 )
|
||||
{
|
||||
//problem with coordinate transformation
|
||||
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
startCapturing();
|
||||
}
|
||||
else if ( e->button() == Qt::RightButton )
|
||||
{
|
||||
// End of string
|
||||
deleteTempRubberBand();
|
||||
|
||||
//lines: bail out if there are not at least two vertices
|
||||
if ( mode() == CaptureLine && size() < 2 )
|
||||
{
|
||||
stopCapturing();
|
||||
return;
|
||||
}
|
||||
|
||||
//polygons: bail out if there are not at least two vertices
|
||||
if ( mode() == CapturePolygon && size() < 3 )
|
||||
{
|
||||
stopCapturing();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mode() == CapturePolygon )
|
||||
{
|
||||
closePolygon();
|
||||
}
|
||||
|
||||
//create QgsFeature with wkb representation
|
||||
std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
|
||||
|
||||
//does compoundcurve contain circular strings?
|
||||
//does provider support circular strings?
|
||||
bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
|
||||
bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
|
||||
|
||||
QList<QgsPointLocator::Match> snappingMatchesList;
|
||||
QgsCurve *curveToAdd = nullptr;
|
||||
if ( hasCurvedSegments && providerSupportsCurvedSegments )
|
||||
{
|
||||
curveToAdd = captureCurve()->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
curveToAdd = captureCurve()->curveToLine();
|
||||
snappingMatchesList = snappingMatches();
|
||||
}
|
||||
|
||||
if ( mode() == CaptureLine )
|
||||
{
|
||||
QgsGeometry g( curveToAdd );
|
||||
f->setGeometry( g );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsCurvePolygon *poly = nullptr;
|
||||
if ( hasCurvedSegments && providerSupportsCurvedSegments )
|
||||
//can only add topological points if background layer is editable...
|
||||
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
|
||||
{
|
||||
poly = new QgsCurvePolygon();
|
||||
}
|
||||
else
|
||||
{
|
||||
poly = new QgsPolygon();
|
||||
}
|
||||
poly->setExteriorRing( curveToAdd );
|
||||
QgsGeometry g( poly );
|
||||
f->setGeometry( g );
|
||||
|
||||
QgsGeometry featGeom = f->geometry();
|
||||
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
|
||||
f->setGeometry( featGeom );
|
||||
if ( avoidIntersectionsReturn == 1 )
|
||||
{
|
||||
//not a polygon type. Impossible to get there
|
||||
}
|
||||
if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
|
||||
{
|
||||
emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), QgsMessageBar::CRITICAL );
|
||||
stopCapturing();
|
||||
return;
|
||||
vl->addTopologicalPoints( f->geometry() );
|
||||
}
|
||||
}
|
||||
f->setValid( true );
|
||||
|
||||
if ( addFeature( vlayer, f.get(), false ) )
|
||||
{
|
||||
//add points to other features to keep topology up-to-date
|
||||
bool topologicalEditing = QgsProject::instance()->topologicalEditing();
|
||||
|
||||
//use always topological editing for avoidIntersection.
|
||||
//Otherwise, no way to guarantee the geometries don't have a small gap in between.
|
||||
QList<QgsVectorLayer *> intersectionLayers = QgsProject::instance()->avoidIntersectionsLayers();
|
||||
bool avoidIntersection = !intersectionLayers.isEmpty();
|
||||
if ( avoidIntersection ) //try to add topological points also to background layers
|
||||
{
|
||||
Q_FOREACH ( QgsVectorLayer *vl, intersectionLayers )
|
||||
{
|
||||
//can only add topological points if background layer is editable...
|
||||
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() )
|
||||
{
|
||||
vl->addTopologicalPoints( f->geometry() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( topologicalEditing )
|
||||
{
|
||||
vlayer->addTopologicalPoints( f->geometry() );
|
||||
}
|
||||
}
|
||||
|
||||
stopCapturing();
|
||||
}
|
||||
else if ( topologicalEditing )
|
||||
{
|
||||
vlayer->addTopologicalPoints( f->geometry() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,21 +13,19 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsmaptoolcapture.h"
|
||||
#include "qgis_app.h"
|
||||
#include "qgsmaptooldigitizefeature.h"
|
||||
|
||||
//! This tool adds new point/line/polygon features to already existing vector layers
|
||||
class APP_EXPORT QgsMapToolAddFeature : public QgsMapToolCapture
|
||||
class APP_EXPORT QgsMapToolAddFeature : public QgsMapToolDigitizeFeature
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
//! \since QGIS 2.12
|
||||
QgsMapToolAddFeature( QgsMapCanvas *canvas, CaptureMode mode );
|
||||
|
||||
void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;
|
||||
|
||||
bool addFeature( QgsVectorLayer *vlayer, QgsFeature *f, bool showModal = true );
|
||||
void activate() override;
|
||||
|
||||
void digitized( QgsFeature *f ) override;
|
||||
|
||||
protected:
|
||||
|
||||
|
308
src/app/qgsmaptooldigitizefeature.cpp
Normal file
308
src/app/qgsmaptooldigitizefeature.cpp
Normal file
@ -0,0 +1,308 @@
|
||||
/***************************************************************************
|
||||
qgsmaptooldigitizefeature- %{Cpp:License:ClassName}
|
||||
|
||||
---------------------
|
||||
begin : 7.12.2017
|
||||
copyright : (C) 2017 by david
|
||||
email : [your-email-here]
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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 "qgsmaptooldigitizefeature.h"
|
||||
#include "qgsadvanceddigitizingdockwidget.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsattributedialog.h"
|
||||
#include "qgsexception.h"
|
||||
#include "qgscurvepolygon.h"
|
||||
#include "qgsfields.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgslinestring.h"
|
||||
#include "qgsmultipoint.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmapmouseevent.h"
|
||||
#include "qgspolygon.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsfeatureaction.h"
|
||||
#include "qgisapp.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QSettings>
|
||||
|
||||
QgsMapToolDigitizeFeature::QgsMapToolDigitizeFeature( QgsMapCanvas *canvas, CaptureMode mode )
|
||||
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), mode )
|
||||
, mCheckGeometryType( true )
|
||||
{
|
||||
mToolName = tr( "Add feature" );
|
||||
connect( QgisApp::instance(), &QgisApp::newProject, this, &QgsMapToolDigitizeFeature::stopCapturing );
|
||||
connect( QgisApp::instance(), &QgisApp::projectRead, this, &QgsMapToolDigitizeFeature::stopCapturing );
|
||||
}
|
||||
|
||||
void QgsMapToolDigitizeFeature::digitized( QgsFeature *f )
|
||||
{
|
||||
emit digitizingFinished( static_cast< const QgsFeature & > ( *f ) );
|
||||
}
|
||||
|
||||
void QgsMapToolDigitizeFeature::activate()
|
||||
{
|
||||
QgsVectorLayer *vlayer = currentVectorLayer();
|
||||
if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
|
||||
{
|
||||
QgsFeature f;
|
||||
digitized( &f );
|
||||
return;
|
||||
}
|
||||
|
||||
QgsMapToolCapture::activate();
|
||||
}
|
||||
|
||||
bool QgsMapToolDigitizeFeature::checkGeometryType() const
|
||||
{
|
||||
return mCheckGeometryType;
|
||||
}
|
||||
|
||||
void QgsMapToolDigitizeFeature::setCheckGeometryType( bool checkGeometryType )
|
||||
{
|
||||
mCheckGeometryType = checkGeometryType;
|
||||
}
|
||||
|
||||
void QgsMapToolDigitizeFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
QgsVectorLayer *vlayer = currentVectorLayer();
|
||||
|
||||
if ( !vlayer )
|
||||
{
|
||||
notifyNotVectorLayer();
|
||||
return;
|
||||
}
|
||||
|
||||
QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
|
||||
|
||||
QgsVectorDataProvider *provider = vlayer->dataProvider();
|
||||
|
||||
if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
|
||||
{
|
||||
emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !vlayer->isEditable() )
|
||||
{
|
||||
notifyNotEditableLayer();
|
||||
return;
|
||||
}
|
||||
|
||||
// POINT CAPTURING
|
||||
if ( mode() == CapturePoint )
|
||||
{
|
||||
if ( e->button() != Qt::LeftButton )
|
||||
return;
|
||||
|
||||
//check we only use this tool for point/multipoint layers
|
||||
if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QgsPointXY savePoint; //point in layer coordinates
|
||||
try
|
||||
{
|
||||
QgsPoint fetchPoint;
|
||||
int res;
|
||||
res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
|
||||
if ( res == 0 )
|
||||
{
|
||||
savePoint = QgsPointXY( fetchPoint.x(), fetchPoint.y() );
|
||||
}
|
||||
else
|
||||
{
|
||||
savePoint = toLayerCoordinates( vlayer, e->mapPoint() );
|
||||
}
|
||||
QgsDebugMsg( "savePoint = " + savePoint.toString() );
|
||||
}
|
||||
catch ( QgsCsException &cse )
|
||||
{
|
||||
Q_UNUSED( cse );
|
||||
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//only do the rest for provider with feature addition support
|
||||
//note that for the grass provider, this will return false since
|
||||
//grass provider has its own mechanism of feature addition
|
||||
if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures )
|
||||
{
|
||||
QgsFeature f( vlayer->fields(), 0 );
|
||||
|
||||
QgsGeometry g;
|
||||
if ( layerWKBType == QgsWkbTypes::Point )
|
||||
{
|
||||
g = QgsGeometry::fromPointXY( savePoint );
|
||||
}
|
||||
else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
g = QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
|
||||
}
|
||||
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
|
||||
}
|
||||
else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
|
||||
{
|
||||
QgsMultiPoint *mp = new QgsMultiPoint();
|
||||
mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) );
|
||||
g = QgsGeometry( mp );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if layer supports more types (mCheckGeometryType is false)
|
||||
g = QgsGeometry::fromPointXY( savePoint );
|
||||
}
|
||||
|
||||
if ( QgsWkbTypes::hasM( layerWKBType ) )
|
||||
{
|
||||
g.get()->addMValue();
|
||||
}
|
||||
|
||||
f.setGeometry( g );
|
||||
f.setValid( true );
|
||||
|
||||
digitized( &f );
|
||||
|
||||
// we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
|
||||
cadDockWidget()->clearPoints();
|
||||
}
|
||||
}
|
||||
|
||||
// LINE AND POLYGON CAPTURING
|
||||
else if ( mode() == CaptureLine || mode() == CapturePolygon )
|
||||
{
|
||||
//check we only use the line tool for line/multiline layers
|
||||
if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//check we only use the polygon tool for polygon/multipolygon layers
|
||||
if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
|
||||
{
|
||||
emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
//add point to list and to rubber band
|
||||
if ( e->button() == Qt::LeftButton )
|
||||
{
|
||||
int error = addVertex( e->mapPoint(), e->mapPointMatch() );
|
||||
if ( error == 1 )
|
||||
{
|
||||
//current layer is not a vector layer
|
||||
return;
|
||||
}
|
||||
else if ( error == 2 )
|
||||
{
|
||||
//problem with coordinate transformation
|
||||
emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING );
|
||||
return;
|
||||
}
|
||||
|
||||
startCapturing();
|
||||
}
|
||||
else if ( e->button() == Qt::RightButton )
|
||||
{
|
||||
// End of string
|
||||
deleteTempRubberBand();
|
||||
|
||||
//lines: bail out if there are not at least two vertices
|
||||
if ( mode() == CaptureLine && size() < 2 )
|
||||
{
|
||||
stopCapturing();
|
||||
return;
|
||||
}
|
||||
|
||||
//polygons: bail out if there are not at least two vertices
|
||||
if ( mode() == CapturePolygon && size() < 3 )
|
||||
{
|
||||
stopCapturing();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mode() == CapturePolygon )
|
||||
{
|
||||
closePolygon();
|
||||
}
|
||||
|
||||
//create QgsFeature with wkb representation
|
||||
std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
|
||||
|
||||
//does compoundcurve contain circular strings?
|
||||
//does provider support circular strings?
|
||||
bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
|
||||
bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
|
||||
|
||||
QList<QgsPointLocator::Match> snappingMatchesList;
|
||||
QgsCurve *curveToAdd = nullptr;
|
||||
if ( hasCurvedSegments && providerSupportsCurvedSegments )
|
||||
{
|
||||
curveToAdd = captureCurve()->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
curveToAdd = captureCurve()->curveToLine();
|
||||
snappingMatchesList = snappingMatches();
|
||||
}
|
||||
|
||||
if ( mode() == CaptureLine )
|
||||
{
|
||||
QgsGeometry g( curveToAdd );
|
||||
f->setGeometry( g );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsCurvePolygon *poly = nullptr;
|
||||
if ( hasCurvedSegments && providerSupportsCurvedSegments )
|
||||
{
|
||||
poly = new QgsCurvePolygon();
|
||||
}
|
||||
else
|
||||
{
|
||||
poly = new QgsPolygon();
|
||||
}
|
||||
poly->setExteriorRing( curveToAdd );
|
||||
QgsGeometry g( poly );
|
||||
f->setGeometry( g );
|
||||
|
||||
QgsGeometry featGeom = f->geometry();
|
||||
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
|
||||
f->setGeometry( featGeom );
|
||||
if ( avoidIntersectionsReturn == 1 )
|
||||
{
|
||||
//not a polygon type. Impossible to get there
|
||||
}
|
||||
if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
|
||||
{
|
||||
emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), QgsMessageBar::CRITICAL );
|
||||
stopCapturing();
|
||||
return;
|
||||
}
|
||||
}
|
||||
f->setValid( true );
|
||||
|
||||
digitized( f.get() );
|
||||
|
||||
stopCapturing();
|
||||
}
|
||||
}
|
||||
}
|
61
src/app/qgsmaptooldigitizefeature.h
Normal file
61
src/app/qgsmaptooldigitizefeature.h
Normal file
@ -0,0 +1,61 @@
|
||||
/***************************************************************************
|
||||
qgsmaptooldigitizegeometry - %{Cpp:License:ClassName}
|
||||
|
||||
---------------------
|
||||
begin : 7.12.2017
|
||||
copyright : (C) 2017 by david
|
||||
email : [your-email-here]
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSMAPTOOLDIGITIZEFEATURE_H
|
||||
#define QGSMAPTOOLDIGITIZEFEATURE_H
|
||||
|
||||
#include "qgsmaptoolcapture.h"
|
||||
#include "qgis_app.h"
|
||||
|
||||
//! This tool digitizes geometry of new point/line/polygon features on already existing vector layers
|
||||
class APP_EXPORT QgsMapToolDigitizeFeature : public QgsMapToolCapture
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
//! \since QGIS 2.12
|
||||
QgsMapToolDigitizeFeature( QgsMapCanvas *canvas, CaptureMode mode );
|
||||
|
||||
void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;
|
||||
|
||||
virtual void digitized( QgsFeature *f );
|
||||
|
||||
virtual void activate() override;
|
||||
|
||||
signals:
|
||||
void digitizingFinished( const QgsFeature & );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Check if CaptureMode matches layer type. Default is true.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
bool checkGeometryType() const;
|
||||
|
||||
/**
|
||||
* Check if CaptureMode matches layer type. Default is true.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
void setCheckGeometryType( bool checkGeometryType );
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Check if CaptureMode matches layer type. Default is true.
|
||||
* \since QGIS 2.12 */
|
||||
bool mCheckGeometryType;
|
||||
};
|
||||
|
||||
#endif // QGSMAPTOOLDIGITIZEFEATURE_H
|
Loading…
x
Reference in New Issue
Block a user