diff --git a/images/images.qrc b/images/images.qrc index 93e8d66b3ee..c171859de5b 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -273,8 +273,8 @@ themes/default/mActionSignMinus.png themes/default/mActionSignPlus.png themes/default/mActionSimplify.png - themes/default/mActionSplitFeatures.png themes/default/mActionSplitFeatures.svg + themes/default/mActionSplitParts.svg themes/default/mActionSum.png themes/default/mActionTextAnnotation.png themes/default/mActionToggleEditing.png diff --git a/images/themes/default/mActionSplitFeatures.png b/images/themes/default/mActionSplitFeatures.png deleted file mode 100644 index 583c65b63f1..00000000000 Binary files a/images/themes/default/mActionSplitFeatures.png and /dev/null differ diff --git a/images/themes/default/mActionSplitParts.svg b/images/themes/default/mActionSplitParts.svg new file mode 100644 index 00000000000..e917c38d501 --- /dev/null +++ b/images/themes/default/mActionSplitParts.svg @@ -0,0 +1,790 @@ + + + + + polygon split + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + polygon split + 2011-04-20 + + + Robert Szczepanek, Anita Graser + + + + + + + + + + polygon split + + + GIS icons 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/gui/qgisinterface.sip b/python/gui/qgisinterface.sip index 6659c69bad9..08a48e1fb7d 100644 --- a/python/gui/qgisinterface.sip +++ b/python/gui/qgisinterface.sip @@ -388,6 +388,7 @@ class QgisInterface : QObject virtual QAction *actionDeleteSelected() = 0; virtual QAction *actionMoveFeature() = 0; virtual QAction *actionSplitFeatures() = 0; + virtual QAction *actionSplitParts() = 0; virtual QAction *actionAddRing() = 0; virtual QAction *actionAddPart() = 0; virtual QAction *actionSimplifyFeature() = 0; diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index a54cea7ea27..a335f482df5 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -76,6 +76,7 @@ SET(QGIS_APP_SRCS qgsmaptoolselectutils.cpp qgsmaptoolsimplify.cpp qgsmaptoolsplitfeatures.cpp + qgsmaptoolsplitparts.cpp qgsmaptoolsvgannotation.cpp qgsmaptooltextannotation.cpp qgsmaptoolvertexedit.cpp @@ -226,6 +227,7 @@ SET (QGIS_APP_MOC_HDRS qgsmaptoolselectrectangle.h qgsmaptoolsimplify.h qgsmaptoolsplitfeatures.h + qgsmaptoolsplitparts.h qgsmaptoolvertexedit.h nodetool/qgsmaptoolnodetool.h diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 096d5b158e8..e4db5171431 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -245,6 +245,7 @@ #include "qgsmaptoolreshape.h" #include "qgsmaptoolrotatepointsymbols.h" #include "qgsmaptoolsplitfeatures.h" +#include "qgsmaptoolsplitparts.h" #include "qgsmaptooltextannotation.h" #include "qgsmaptoolvertexedit.h" #include "qgsmaptoolzoom.h" @@ -806,6 +807,7 @@ QgisApp::~QgisApp() delete mMapTools.mShowHideLabels; delete mMapTools.mSimplifyFeature; delete mMapTools.mSplitFeatures; + delete mMapTools.mSplitParts; delete mMapTools.mSvgAnnotation; delete mMapTools.mTextAnnotation; @@ -963,6 +965,7 @@ void QgisApp::createActions() connect( mActionReshapeFeatures, SIGNAL( triggered() ), this, SLOT( reshapeFeatures() ) ); connect( mActionSplitFeatures, SIGNAL( triggered() ), this, SLOT( splitFeatures() ) ); + connect( mActionSplitParts, SIGNAL( triggered() ), this, SLOT( splitParts() ) ); connect( mActionDeleteSelected, SIGNAL( triggered() ), this, SLOT( deleteSelected() ) ); connect( mActionAddRing, SIGNAL( triggered() ), this, SLOT( addRing() ) ); connect( mActionAddPart, SIGNAL( triggered() ), this, SLOT( addPart() ) ); @@ -1233,6 +1236,7 @@ void QgisApp::createActionGroups() #endif mMapToolGroup->addAction( mActionReshapeFeatures ); mMapToolGroup->addAction( mActionSplitFeatures ); + mMapToolGroup->addAction( mActionSplitParts ); mMapToolGroup->addAction( mActionDeleteSelected ); mMapToolGroup->addAction( mActionAddRing ); mMapToolGroup->addAction( mActionAddPart ); @@ -1774,6 +1778,7 @@ void QgisApp::setTheme( QString theThemeName ) mActionRotateFeature->setIcon( QgsApplication::getThemeIcon( "/mActionRotateFeature.png" ) ); mActionReshapeFeatures->setIcon( QgsApplication::getThemeIcon( "/mActionReshape.png" ) ); mActionSplitFeatures->setIcon( QgsApplication::getThemeIcon( "/mActionSplitFeatures.svg" ) ); + mActionSplitParts->setIcon( QgsApplication::getThemeIcon( "/mActionSplitParts.svg" ) ); mActionDeleteSelected->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteSelected.svg" ) ); mActionNodeTool->setIcon( QgsApplication::getThemeIcon( "/mActionNodeTool.png" ) ); mActionSimplifyFeature->setIcon( QgsApplication::getThemeIcon( "/mActionSimplify.png" ) ); @@ -1997,6 +2002,8 @@ void QgisApp::createCanvasTools() mMapTools.mReshapeFeatures->setAction( mActionReshapeFeatures ); mMapTools.mSplitFeatures = new QgsMapToolSplitFeatures( mMapCanvas ); mMapTools.mSplitFeatures->setAction( mActionSplitFeatures ); + mMapTools.mSplitParts = new QgsMapToolSplitParts( mMapCanvas ); + mMapTools.mSplitParts->setAction( mActionSplitParts ); mMapTools.mSelect = new QgsMapToolSelect( mMapCanvas ); mMapTools.mSelect->setAction( mActionSelect ); mMapTools.mSelectRectangle = new QgsMapToolSelectRectangle( mMapCanvas ); @@ -5369,6 +5376,11 @@ void QgisApp::splitFeatures() mMapCanvas->setMapTool( mMapTools.mSplitFeatures ); } +void QgisApp::splitParts() +{ + mMapCanvas->setMapTool( mMapTools.mSplitParts ); +} + void QgisApp::reshapeFeatures() { mMapCanvas->setMapTool( mMapTools.mReshapeFeatures ); @@ -8257,6 +8269,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionReshapeFeatures->setEnabled( false ); mActionOffsetCurve->setEnabled( false ); mActionSplitFeatures->setEnabled( false ); + mActionSplitParts->setEnabled( false ); mActionMergeFeatures->setEnabled( false ); mActionMergeFeatureAttributes->setEnabled( false ); mActionRotatePointSymbols->setEnabled( false ); @@ -8384,6 +8397,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionAddRing->setEnabled( false ); mActionReshapeFeatures->setEnabled( false ); mActionSplitFeatures->setEnabled( false ); + mActionSplitParts->setEnabled( false ); mActionSimplifyFeature->setEnabled( false ); mActionDeleteRing->setEnabled( false ); mActionRotatePointSymbols->setEnabled( false ); @@ -8405,6 +8419,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionReshapeFeatures->setEnabled( isEditable && canAddFeatures ); mActionSplitFeatures->setEnabled( isEditable && canAddFeatures ); + mActionSplitParts->setEnabled( isEditable && canAddFeatures ); mActionSimplifyFeature->setEnabled( isEditable && canAddFeatures ); mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes ); @@ -8418,6 +8433,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionAddRing->setEnabled( isEditable && canAddFeatures ); mActionReshapeFeatures->setEnabled( isEditable && canAddFeatures ); mActionSplitFeatures->setEnabled( isEditable && canAddFeatures ); + mActionSplitParts->setEnabled( isEditable && canAddFeatures ); mActionSimplifyFeature->setEnabled( isEditable && canAddFeatures ); mActionDeleteRing->setEnabled( isEditable && canAddFeatures ); mActionOffsetCurve->setEnabled( false ); @@ -8502,6 +8518,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionSimplifyFeature->setEnabled( false ); mActionReshapeFeatures->setEnabled( false ); mActionSplitFeatures->setEnabled( false ); + mActionSplitParts->setEnabled( false ); mActionLabeling->setEnabled( false ); //NOTE: This check does not really add any protection, as it is called on load not on layer select/activate diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 49e256bcee6..0942891b281 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -262,6 +262,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QAction *actionMoveFeature() { return mActionMoveFeature; } QAction *actionRotateFeature() { return mActionRotateFeature;} QAction *actionSplitFeatures() { return mActionSplitFeatures; } + QAction *actionSplitParts() { return mActionSplitParts; } QAction *actionAddRing() { return mActionAddRing; } QAction *actionAddPart() { return mActionAddPart; } QAction *actionSimplifyFeature() { return mActionSimplifyFeature; } @@ -919,6 +920,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow void reshapeFeatures(); //! activates the split features tool void splitFeatures(); + //! activates the split parts tool + void splitParts(); //! activates the add ring tool void addRing(); //! activates the add part tool @@ -1332,6 +1335,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsMapTool* mOffsetCurve; QgsMapTool* mReshapeFeatures; QgsMapTool* mSplitFeatures; + QgsMapTool* mSplitParts; QgsMapTool* mSelect; QgsMapTool* mSelectRectangle; QgsMapTool* mSelectPolygon; diff --git a/src/app/qgisappinterface.cpp b/src/app/qgisappinterface.cpp index 0abfb0654ad..6f38b8afa42 100644 --- a/src/app/qgisappinterface.cpp +++ b/src/app/qgisappinterface.cpp @@ -482,6 +482,7 @@ QAction *QgisAppInterface::actionAddFeature() { return qgis->actionAddFeature(); QAction *QgisAppInterface::actionDeleteSelected() { return qgis->actionDeleteSelected(); } QAction *QgisAppInterface::actionMoveFeature() { return qgis->actionMoveFeature(); } QAction *QgisAppInterface::actionSplitFeatures() { return qgis->actionSplitFeatures(); } +QAction *QgisAppInterface::actionSplitParts() { return qgis->actionSplitParts(); } QAction *QgisAppInterface::actionAddRing() { return qgis->actionAddRing(); } QAction *QgisAppInterface::actionAddPart() { return qgis->actionAddPart(); } QAction *QgisAppInterface::actionSimplifyFeature() { return qgis->actionSimplifyFeature(); } diff --git a/src/app/qgisappinterface.h b/src/app/qgisappinterface.h index 840e2998787..61fdffec0e3 100644 --- a/src/app/qgisappinterface.h +++ b/src/app/qgisappinterface.h @@ -332,6 +332,7 @@ class APP_EXPORT QgisAppInterface : public QgisInterface virtual QAction *actionDeleteSelected(); virtual QAction *actionMoveFeature(); virtual QAction *actionSplitFeatures(); + virtual QAction *actionSplitParts(); virtual QAction *actionAddRing(); virtual QAction *actionAddPart(); virtual QAction *actionSimplifyFeature(); diff --git a/src/app/qgsmaptoolsplitparts.cpp b/src/app/qgsmaptoolsplitparts.cpp new file mode 100644 index 00000000000..d24bdaf7a38 --- /dev/null +++ b/src/app/qgsmaptoolsplitparts.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + qgsmaptoolsplitparts.h + --------------------- + begin : April 2013 + copyright : (C) 2013 Denis Rouzaud + email : denis.rouzaud@gmail.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 "qgisapp.h" +#include "qgsmessagebar.h" +#include "qgsmapcanvas.h" +#include "qgsproject.h" +#include "qgsmaptoolsplitparts.h" +#include "qgsvectorlayer.h" + +#include + +QgsMapToolSplitParts::QgsMapToolSplitParts( QgsMapCanvas* canvas ): QgsMapToolCapture( canvas, QgsMapToolCapture::CaptureLine ) +{ + +} + +QgsMapToolSplitParts::~QgsMapToolSplitParts() +{ + +} + +void QgsMapToolSplitParts::canvasReleaseEvent( QMouseEvent * e ) +{ + //check if we operate on a vector layer + QgsVectorLayer *vlayer = qobject_cast( 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 + QgisApp::instance()->messageBar()->pushMessage( + tr( "Coordinate transform error" ), + tr( "Cannot transform the point to the layers coordinate system" ), + QgsMessageBar::INFO, + QgisApp::instance()->messageTimeout() ); + return; + } + + startCapturing(); + } + else if ( e->button() == Qt::RightButton ) + { + deleteTempRubberBand(); + + //bring up dialog if a split was not possible (polygon) or only done once (line) + int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); + vlayer->beginEditCommand( tr( "Parts split" ) ); + int returnCode = vlayer->splitParts( points(), topologicalEditing ); + vlayer->endEditCommand(); + if ( returnCode == 4 ) + { + QgisApp::instance()->messageBar()->pushMessage( + tr( "No part split done" ), + tr( "If there are selected parts, the split tool only applies to the selected ones. If you like to split all parts under the split line, clear the selection" ), + QgsMessageBar::WARNING, + QgisApp::instance()->messageTimeout() ); + } + else if ( returnCode == 3 ) + { + QgisApp::instance()->messageBar()->pushMessage( + tr( "No part split done" ), + tr( "Cut edges detected. Make sure the line splits parts into multiple parts." ), + QgsMessageBar::WARNING, + QgisApp::instance()->messageTimeout() ); + } + else if ( returnCode == 7 ) + { + QgisApp::instance()->messageBar()->pushMessage( + tr( "No part split done" ), + tr( "The geometry is invalid. Please repair before trying to split it." ) , + QgsMessageBar::WARNING, + QgisApp::instance()->messageTimeout() ); + } + else if ( returnCode != 0 ) + { + //several intersections but only one split (most likely line) + QgisApp::instance()->messageBar()->pushMessage( + tr( "Split error" ), + tr( "An error occured during feature splitting" ), + QgsMessageBar::WARNING, + QgisApp::instance()->messageTimeout() ); + } + + stopCapturing(); + } +} diff --git a/src/app/qgsmaptoolsplitparts.h b/src/app/qgsmaptoolsplitparts.h new file mode 100644 index 00000000000..d6b34498d7a --- /dev/null +++ b/src/app/qgsmaptoolsplitparts.h @@ -0,0 +1,31 @@ +/*************************************************************************** + qgsmaptoolsplitparts.h + --------------------- + begin : April 2013 + copyright : (C) 2013 Denis Rouzaud + email : denis.rouzaud@gmail.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. * + * * + ***************************************************************************/ + +#ifndef QGSMAPTOOLSPLITPARTS_H +#define QGSMAPTOOLSPLITPARTS_H + +#include "qgsmaptoolcapture.h" + +/**A map tool that draws a line and splits the parts cut by the line*/ +class QgsMapToolSplitParts: public QgsMapToolCapture +{ + Q_OBJECT + public: + QgsMapToolSplitParts( QgsMapCanvas* canvas ); + virtual ~QgsMapToolSplitParts(); + void canvasReleaseEvent( QMouseEvent * e ); +}; + +#endif diff --git a/src/core/qgsgeometry.cpp b/src/core/qgsgeometry.cpp index 96593b49bd8..6044fbfd25e 100644 --- a/src/core/qgsgeometry.cpp +++ b/src/core/qgsgeometry.cpp @@ -2935,25 +2935,6 @@ int QgsGeometry::addPart( const QList &points ) return 2; } - if ( !isMultipart() && !convertToMultiType() ) - { - QgsDebugMsg( "could not convert to multipart" ); - return 1; - } - - //create geos geometry from wkb if not already there - if ( mDirtyGeos ) - { - exportWkbToGeos(); - } - - if ( !mGeos ) - { - QgsDebugMsg( "GEOS geometry not available!" ); - return 4; - } - - int geosType = GEOSGeomTypeId( mGeos ); GEOSGeometry *newPart = 0; switch ( geomType ) @@ -2996,6 +2977,39 @@ int QgsGeometry::addPart( const QList &points ) return 2; } + return addPart( newPart ); +} + +int QgsGeometry::addPart( QgsGeometry * newPart ) +{ + const GEOSGeometry * geosPart = newPart->asGeos(); + return addPart( GEOSGeom_clone( geosPart ) ); +} + +int QgsGeometry::addPart( GEOSGeometry * newPart ) +{ + QGis::GeometryType geomType = type(); + + if ( !isMultipart() && !convertToMultiType() ) + { + QgsDebugMsg( "could not convert to multipart" ); + return 1; + } + + //create geos geometry from wkb if not already there + if ( mDirtyGeos ) + { + exportWkbToGeos(); + } + + if ( !mGeos ) + { + QgsDebugMsg( "GEOS geometry not available!" ); + return 4; + } + + int geosType = GEOSGeomTypeId( mGeos ); + Q_ASSERT( newPart ); try @@ -3023,8 +3037,8 @@ int QgsGeometry::addPart( const QList &points ) { const GEOSGeometry *partN = GEOSGetGeometryN( mGeos, i ); - if ( geomType == QGis::Polygon && !GEOSDisjoint( partN, newPart ) ) - //bail out if new polygon is not disjoint with existing ones + if ( geomType == QGis::Polygon && GEOSOverlaps( partN, newPart ) ) + //bail out if new polygon overlaps with existing ones break; parts << GEOSGeom_clone( partN ); @@ -3036,11 +3050,16 @@ int QgsGeometry::addPart( const QList &points ) for ( int i = 0; i < parts.size(); i++ ) GEOSGeom_destroy( parts[i] ); - QgsDebugMsg( "new polygon part not disjoint" ); + QgsDebugMsg( "new polygon part overlaps" ); return 3; } - parts << newPart; + int nPartGeoms = GEOSGetNumGeometries( newPart ); + for( int i = 0; i < nPartGeoms; ++i ) + { + parts << GEOSGeom_clone( GEOSGetGeometryN( newPart, i ) ); + } + GEOSGeom_destroy( newPart ); GEOSGeom_destroy( mGeos ); diff --git a/src/core/qgsgeometry.h b/src/core/qgsgeometry.h index 54604628ed4..23e17c0238f 100644 --- a/src/core/qgsgeometry.h +++ b/src/core/qgsgeometry.h @@ -267,6 +267,16 @@ class CORE_EXPORT QgsGeometry not disjoint with existing polygons of the feature*/ int addPart( const QList &points ); + /**Adds a new island polygon to a multipolygon feature + @return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring + not disjoint with existing polygons of the feature*/ + int addPart( GEOSGeometry *newPart ); + + /**Adds a new island polygon to a multipolygon feature + @return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring + not disjoint with existing polygons of the feature*/ + int addPart( QgsGeometry *newPart ); + /**Translate this geometry by dx, dy @return 0 in case of success*/ int translate( double dx, double dy ); diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 025cf146302..1e96e20d2c5 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -1378,6 +1378,15 @@ int QgsVectorLayer::translateFeature( QgsFeatureId featureId, double dx, double return utils.translateFeature( featureId, dx, dy ); } +int QgsVectorLayer::splitParts( const QList& splitLine, bool topologicalEditing ) +{ + if ( !mEditBuffer || !mDataProvider ) + return -1; + + QgsVectorLayerEditUtils utils( this ); + return utils.splitParts( splitLine, topologicalEditing ); +} + int QgsVectorLayer::splitFeatures( const QList& splitLine, bool topologicalEditing ) { if ( !mEditBuffer || !mDataProvider ) diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 5fbeff8c55e..ee1ff0d90b0 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -895,6 +895,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer @return 0 in case of success*/ int translateFeature( QgsFeatureId featureId, double dx, double dy ); + /**Splits parts cut by the given line + * @param splitLine line that splits the layer features + * @param topologicalEditing true if topological editing is enabled + * @return + * 0 in case of success, + * 4 if there is a selection but no feature split + */ + int splitParts( const QList& splitLine, bool topologicalEditing = false ); + /**Splits features cut by the given line * @param splitLine line that splits the layer features * @param topologicalEditing true if topological editing is enabled diff --git a/src/core/qgsvectorlayereditutils.cpp b/src/core/qgsvectorlayereditutils.cpp index 6f1660ff854..dfcde35fde5 100644 --- a/src/core/qgsvectorlayereditutils.cpp +++ b/src/core/qgsvectorlayereditutils.cpp @@ -17,6 +17,7 @@ #include "qgsvectordataprovider.h" #include "qgsgeometrycache.h" #include "qgsvectorlayereditbuffer.h" +#include "qgslogger.h" #include @@ -295,6 +296,132 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList& splitLine, bo return returnCode; } +int QgsVectorLayerEditUtils::splitParts( const QList& splitLine, bool topologicalEditing ) +{ + if ( !L->hasGeometryType() ) + return 4; + + double xMin, yMin, xMax, yMax; + QgsRectangle bBox; //bounding box of the split line + int returnCode = 0; + int splitFunctionReturn; //return code of QgsGeometry::splitGeometry + int numberOfSplittedParts = 0; + + QgsFeatureList featureList; + const QgsFeatureIds selectedIds = L->selectedFeaturesIds(); + + if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection + { + featureList = L->selectedFeatures(); + } + else //else consider all the feature that intersect the bounding box of the split line + { + if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 ) + { + bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); + bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); + } + else + { + return 1; + } + + if ( bBox.isEmpty() ) + { + //if the bbox is a line, try to make a square out of it + if ( bBox.width() == 0.0 && bBox.height() > 0 ) + { + bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); + bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); + } + else if ( bBox.height() == 0.0 && bBox.width() > 0 ) + { + bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); + bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); + } + else + { + return 2; + } + } + + QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); + + QgsFeature f; + while ( fit.nextFeature( f ) ) + featureList << QgsFeature( f ); + } + + int addPartRet; + foreach ( const QgsFeature& feat, featureList ) + { + QList newGeometries; + QList topologyTestPoints; + splitFunctionReturn = feat.geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); + if ( splitFunctionReturn == 0 ) + { + //add new parts + for ( int i = 0; i < newGeometries.size(); ++i ) + { + addPartRet = feat.geometry()->addPart( newGeometries.at( i ) ); + if ( addPartRet != 0 ) + break; + } + + // For test only: Exception already thrown here... + // feat.geometry()->asWkb(); + + if ( addPartRet == 0 ) + { + L->editBuffer()->changeGeometry( feat.id(), feat.geometry() ); + } + else + { + // Test addPartRet + switch ( addPartRet ) + { + case 1: + QgsDebugMsg( "Not a multipolygon" ); + break; + + case 2: + QgsDebugMsg( "Not a valid geometry" ); + break; + + case 3: + QgsDebugMsg( "New polygon ring" ); + break; + } + } + L->editBuffer()->changeGeometry( feat.id(), feat.geometry() ); + + if ( topologicalEditing ) + { + QList::const_iterator topol_it = topologyTestPoints.constBegin(); + for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) + { + addTopologicalPoints( *topol_it ); + } + } + ++numberOfSplittedParts; + } + else if ( splitFunctionReturn > 1 ) //1 means no split but also no error + { + returnCode = splitFunctionReturn; + } + + qDeleteAll( newGeometries ); + } + + if ( numberOfSplittedParts == 0 && selectedIds.size() > 0 ) + { + //There is a selection but no feature has been split. + //Maybe user forgot that only the selected features are split + returnCode = 4; + } + + return returnCode; +} int QgsVectorLayerEditUtils::addTopologicalPoints( QgsGeometry* geom ) diff --git a/src/core/qgsvectorlayereditutils.h b/src/core/qgsvectorlayereditutils.h index c2e3a4b6e17..7ce5b013bd8 100644 --- a/src/core/qgsvectorlayereditutils.h +++ b/src/core/qgsvectorlayereditutils.h @@ -74,6 +74,15 @@ class CORE_EXPORT QgsVectorLayerEditUtils @return 0 in case of success*/ int translateFeature( QgsFeatureId featureId, double dx, double dy ); + /** Splits parts cut by the given line + * @param splitLine line that splits the layer feature parts + * @param topologicalEditing true if topological editing is enabled + * @return + * 0 in case of success, + * 4 if there is a selection but no feature split + */ + int splitParts( const QList& splitLine, bool topologicalEditing = false ); + /** Splits features cut by the given line * @param splitLine line that splits the layer features * @param topologicalEditing true if topological editing is enabled diff --git a/src/gui/qgisinterface.h b/src/gui/qgisinterface.h index 798652db55a..cdacf578419 100644 --- a/src/gui/qgisinterface.h +++ b/src/gui/qgisinterface.h @@ -441,6 +441,7 @@ class GUI_EXPORT QgisInterface : public QObject virtual QAction *actionDeleteSelected() = 0; virtual QAction *actionMoveFeature() = 0; virtual QAction *actionSplitFeatures() = 0; + virtual QAction *actionSplitParts() = 0; virtual QAction *actionAddRing() = 0; virtual QAction *actionAddPart() = 0; virtual QAction *actionSimplifyFeature() = 0; diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index ee005ee8fb1..2158e6cad3f 100644 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -245,6 +245,7 @@ + @@ -342,6 +343,7 @@ + @@ -679,12 +681,24 @@ - :/images/themes/default/mActionSplitFeatures.png:/images/themes/default/mActionSplitFeatures.png + :/images/themes/default/mActionSplitFeatures.svg:/images/themes/default/mActionSplitFeatures.svg Split Features + + + true + + + + :/images/themes/default/mActionSplitFeatures.svg:/images/themes/default/mActionSplitFeatures.svg + + + Split Parts + +