Skip to content

Commit

Permalink
Merge pull request #38285 from uclaros/split-add-topo-all-layers
Browse files Browse the repository at this point in the history
Add topological points to all editable layers on split feature
  • Loading branch information
3nids committed Sep 16, 2020
2 parents bc1511c + 02f5c13 commit bc3d8fd
Show file tree
Hide file tree
Showing 9 changed files with 473 additions and 65 deletions.
26 changes: 24 additions & 2 deletions python/core/auto_generated/qgsvectorlayer.sip.in
Expand Up @@ -1604,12 +1604,13 @@ Splits features cut by the given line
changes can be discarded by calling :py:func:`~QgsVectorLayer.rollBack`.
%End

QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, bool preserveCircular = false, bool topologicalEditing = false );
QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints /Out/, bool preserveCircular = false, bool topologicalEditing = false );
%Docstring
Splits features cut by the given curve

:param curve: curve that splits the layer features
:param preserveCircular: whether if circular strings are preserved after splitting
\param[out] topologyTestPoints topological points to be tested against other layers
:param preserveCircular: whether circular strings are preserved after splitting
:param topologicalEditing: ``True`` if topological editing is enabled

:return: QgsGeometry.OperationResult
Expand Down Expand Up @@ -1695,6 +1696,27 @@ editing.
.. versionadded:: 3.10
%End

int addTopologicalPoints( const QgsPointSequence &ps );
%Docstring
Adds a vertex to segments which intersect any of the points ``p`` but don't
already have a vertex there. If a feature already has a vertex at position ``p``,
no additional vertex is inserted. This method is useful for topological
editing.

:param ps: point sequence of the vertices

:return: 0 in case of success

.. note::

Calls to :py:func:`~QgsVectorLayer.addTopologicalPoints` are only valid for layers in which edits have been enabled
by a call to :py:func:`~QgsVectorLayer.startEditing`. Changes made to features using this method are not committed
to the underlying data provider until a :py:func:`~QgsVectorLayer.commitChanges` call is made. Any uncommitted
changes can be discarded by calling :py:func:`~QgsVectorLayer.rollBack`.

.. versionadded:: 3.16
%End


QgsAbstractVectorLayerLabeling *labeling();
%Docstring
Expand Down
42 changes: 34 additions & 8 deletions python/core/auto_generated/qgsvectorlayereditutils.sip.in
Expand Up @@ -205,8 +205,7 @@ Splits features 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
:return: QgsGeometry.OperationResult

.. deprecated:: QGIS 3.12
- will be removed in QGIS 4.0. Use the variant which accepts QgsPoint objects instead of QgsPointXY.
Expand All @@ -219,20 +218,19 @@ Splits features 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
:return: QgsGeometry.OperationResult
%End

QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, bool preserveCircular = false, bool topologicalEditing = false );
QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints /Out/, bool preserveCircular = false, bool topologicalEditing = false );
%Docstring
Splits features cut by the given curve

:param curve: line that splits the layer features
:param preserveCircular: whether if circular strings are preserved after splitting
\param[out] topologyTestPoints topological points to be tested against other layers
:param preserveCircular: whether circular strings are preserved after splitting
:param topologicalEditing: ``True`` if topological editing is enabled

:return: 0 in case of success,
4 if there is a selection but no feature split
:return: QgsGeometry.OperationResult

.. versionadded:: 3.16
%End
Expand All @@ -245,6 +243,10 @@ Adds topological points for every vertex of the geometry.

:return: 0 in case of success

:return: 1 in case of error

:return: 2 in case no vertices needed to be added

.. note::

geom is not going to be modified by the function
Expand All @@ -258,6 +260,10 @@ no additional vertex is inserted. This method is useful for topological
editing.

:return: 0 in case of success

:return: 1 in case of error

:return: 2 in case no vertices needed to be added
%End

int addTopologicalPoints( const QgsPoint &p );
Expand All @@ -269,7 +275,27 @@ editing.

:return: 0 in case of success

:return: 1 in case of error

:return: 2 in case no vertices needed to be added

.. versionadded:: 3.10
%End

int addTopologicalPoints( const QgsPointSequence &ps );
%Docstring
Adds a vertex to segments which intersect point ``p`` but don't
already have a vertex there. If a feature already has a vertex at position p,
no additional vertex is inserted. This method is useful for topological
editing.

:return: 0 in case of success

:return: 1 in case of error

:return: 2 in case vertex already exists or point does not intersect segment

.. versionadded:: 3.16
%End

};
Expand Down
100 changes: 66 additions & 34 deletions src/app/qgsmaptoolsplitfeatures.cpp
Expand Up @@ -92,9 +92,10 @@ void QgsMapToolSplitFeatures::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
deleteTempRubberBand();

//bring up dialog if a split was not possible (polygon) or only done once (line)
int topologicalEditing = QgsProject::instance()->topologicalEditing();
bool topologicalEditing = QgsProject::instance()->topologicalEditing();
QgsPointSequence topologyTestPoints;
vlayer->beginEditCommand( tr( "Features split" ) );
QgsGeometry::OperationResult returnCode = vlayer->splitFeatures( captureCurve(), true, topologicalEditing );
QgsGeometry::OperationResult returnCode = vlayer->splitFeatures( captureCurve(), topologyTestPoints, true, topologicalEditing );
if ( returnCode == QgsGeometry::OperationResult::Success )
{
vlayer->endEditCommand();
Expand All @@ -103,40 +104,71 @@ void QgsMapToolSplitFeatures::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
{
vlayer->destroyEditCommand();
}
if ( returnCode == QgsGeometry::OperationResult::NothingHappened )
{
QgisApp::instance()->messageBar()->pushMessage(
tr( "No features were split" ),
tr( "If there are selected features, the split tool only applies to those. If you would like to split all features under the split line, clear the selection." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
}
else if ( returnCode == QgsGeometry::OperationResult::GeometryEngineError )
{
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "Cut edges detected. Make sure the line splits features into multiple parts." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
}
else if ( returnCode == QgsGeometry::OperationResult::InvalidBaseGeometry )
{
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "The geometry is invalid. Please repair before trying to split it." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
}
else if ( returnCode != QgsGeometry::OperationResult::Success )

switch ( returnCode )
{
//several intersections but only one split (most likely line)
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "An error occurred during splitting." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
case QgsGeometry::OperationResult::Success:
if ( topologicalEditing == true &&
! topologyTestPoints.isEmpty() )
{
//check if we need to add topological points to other layers
QList<QgsVectorLayer *> editableLayers;
const auto layers = canvas()->layers();
for ( QgsMapLayer *layer : layers )
{
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vectorLayer &&
vectorLayer->isEditable() &&
vectorLayer->isSpatial() &&
vectorLayer != vlayer &&
( vectorLayer->geometryType() == QgsWkbTypes::LineGeometry ||
vectorLayer->geometryType() == QgsWkbTypes::PolygonGeometry ) )
{
vectorLayer->beginEditCommand( tr( "Topological points from Features split" ) );
int returnValue = vectorLayer->addTopologicalPoints( topologyTestPoints );
if ( returnValue == 0 )
{
vectorLayer->endEditCommand();
}
else
{
// the layer was not modified, leave the undo buffer intact
vectorLayer->destroyEditCommand();
}
}
}
}
break;
case QgsGeometry::OperationResult::NothingHappened:
QgisApp::instance()->messageBar()->pushMessage(
tr( "No features were split" ),
tr( "If there are selected features, the split tool only applies to those. If you would like to split all features under the split line, clear the selection." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
break;
case QgsGeometry::OperationResult::GeometryEngineError:
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "Cut edges detected. Make sure the line splits features into multiple parts." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
break;
case QgsGeometry::OperationResult::InvalidBaseGeometry:
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "The geometry is invalid. Please repair before trying to split it." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
break;
default:
//several intersections but only one split (most likely line)
QgisApp::instance()->messageBar()->pushMessage(
tr( "No feature split done" ),
tr( "An error occurred during splitting." ),
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
break;
}

stopCapturing();
}
}
17 changes: 14 additions & 3 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -1370,16 +1370,18 @@ QgsGeometry::OperationResult QgsVectorLayer::splitFeatures( const QVector<QgsPoi
QgsGeometry::OperationResult QgsVectorLayer::splitFeatures( const QgsPointSequence &splitLine, bool topologicalEditing )
{
QgsLineString splitLineString( splitLine );
return splitFeatures( &splitLineString, topologicalEditing );
QgsPointSequence topologyTestPoints;
bool preserveCircular = false;
return splitFeatures( &splitLineString, topologyTestPoints, preserveCircular, topologicalEditing );
}

QgsGeometry::OperationResult QgsVectorLayer::splitFeatures( const QgsCurve *curve, bool preserveCircular, bool topologicalEditing )
QgsGeometry::OperationResult QgsVectorLayer::splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints, bool preserveCircular, bool topologicalEditing )
{
if ( !isValid() || !mEditBuffer || !mDataProvider )
return QgsGeometry::OperationResult::LayerNotEditable;

QgsVectorLayerEditUtils utils( this );
return utils.splitFeatures( curve, preserveCircular, topologicalEditing );
return utils.splitFeatures( curve, topologyTestPoints, preserveCircular, topologicalEditing );
}

int QgsVectorLayer::addTopologicalPoints( const QgsGeometry &geom )
Expand All @@ -1405,6 +1407,15 @@ int QgsVectorLayer::addTopologicalPoints( const QgsPoint &p )
return utils.addTopologicalPoints( p );
}

int QgsVectorLayer::addTopologicalPoints( const QgsPointSequence &ps )
{
if ( !mValid || !mEditBuffer || !mDataProvider )
return -1;

QgsVectorLayerEditUtils utils( this );
return utils.addTopologicalPoints( ps );
}

void QgsVectorLayer::setLabeling( QgsAbstractVectorLayerLabeling *labeling )
{
if ( mLabeling == labeling )
Expand Down
20 changes: 18 additions & 2 deletions src/core/qgsvectorlayer.h
Expand Up @@ -1551,7 +1551,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
/**
* Splits features cut by the given curve
* \param curve curve that splits the layer features
* \param preserveCircular whether if circular strings are preserved after splitting
* \param[out] topologyTestPoints topological points to be tested against other layers
* \param preserveCircular whether circular strings are preserved after splitting
* \param topologicalEditing TRUE if topological editing is enabled
* \returns QgsGeometry::OperationResult
*
Expand All @@ -1569,7 +1570,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* changes can be discarded by calling rollBack().
* \since QGIS 3.16
*/
Q_INVOKABLE QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, bool preserveCircular = false, bool topologicalEditing = false );
Q_INVOKABLE QgsGeometry::OperationResult splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints SIP_OUT, bool preserveCircular = false, bool topologicalEditing = false );

/**
* Adds topological points for every vertex of the geometry.
Expand Down Expand Up @@ -1613,6 +1614,21 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
int addTopologicalPoints( const QgsPoint &p );

/**
* Adds a vertex to segments which intersect any of the points \a p but don't
* already have a vertex there. If a feature already has a vertex at position \a p,
* no additional vertex is inserted. This method is useful for topological
* editing.
* \param ps point sequence of the vertices
* \returns 0 in case of success
* \note Calls to addTopologicalPoints() are only valid for layers in which edits have been enabled
* by a call to startEditing(). Changes made to features using this method are not committed
* to the underlying data provider until a commitChanges() call is made. Any uncommitted
* changes can be discarded by calling rollBack().
* \since 3.16
*/
int addTopologicalPoints( const QgsPointSequence &ps );

/**
* Access to const labeling configuration. May be NULLPTR if labeling is not used.
* \note Labels will only be rendered if labelsEnabled() returns TRUE.
Expand Down

0 comments on commit bc3d8fd

Please sign in to comment.