Skip to content

Commit

Permalink
Add topological points to all editable layers on split feature
Browse files Browse the repository at this point in the history
  • Loading branch information
uclaros committed Sep 4, 2020
1 parent 81cf4ee commit ee1f3f6
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 32 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 allready exists or point does not intersect segment

.. versionadded:: 3.16
%End

};
Expand Down
36 changes: 34 additions & 2 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 Down Expand Up @@ -136,6 +137,37 @@ void QgsMapToolSplitFeatures::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
Qgis::Warning,
QgisApp::instance()->messageTimeout() );
}
else if ( returnCode == QgsGeometry::OperationResult::Success &&
topologicalEditing == true &&
! topologyTestPoints.isEmpty() )
{
//success, 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();
}
}
}
}

stopCapturing();
}
Expand Down
17 changes: 14 additions & 3 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -1371,16 +1371,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 @@ -1406,6 +1408,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
47 changes: 40 additions & 7 deletions src/core/qgsvectorlayereditutils.cpp
Expand Up @@ -297,10 +297,12 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QVect
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsPointSequence &splitLine, bool topologicalEditing )
{
QgsLineString lineString( splitLine );
return splitFeatures( &lineString, false, topologicalEditing );
QgsPointSequence topologyTestPoints;
bool preserveCircular = false;
return splitFeatures( &lineString, topologyTestPoints, preserveCircular, topologicalEditing );
}

QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCurve *curve, bool preserveCircular, bool topologicalEditing )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints, bool preserveCircular, bool topologicalEditing )
{
if ( !mLayer->isSpatial() )
return QgsGeometry::InvalidBaseGeometry;
Expand Down Expand Up @@ -362,9 +364,10 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCu
continue;
}
QVector<QgsGeometry> newGeometries;
QgsPointSequence topologyTestPoints;
QgsPointSequence featureTopologyTestPoints;
QgsGeometry featureGeom = feat.geometry();
splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, topologyTestPoints );
splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
topologyTestPoints.append( featureTopologyTestPoints );
if ( splitFunctionReturn == QgsGeometry::OperationResult::Success )
{
//change this geometry
Expand All @@ -380,8 +383,8 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCu

if ( topologicalEditing )
{
QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
QgsPointSequence::const_iterator topol_it = featureTopologyTestPoints.constBegin();
for ( ; topol_it != featureTopologyTestPoints.constEnd(); ++topol_it )
{
addTopologicalPoints( *topol_it );
}
Expand Down Expand Up @@ -598,6 +601,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint &p )
if ( segments.isEmpty() )
return 2;

bool pointsAdded = false;
for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it )
{
QgsFeatureId fid = it.key();
Expand All @@ -615,9 +619,38 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint &p )
{
QgsDebugMsg( QStringLiteral( "failed to insert topo point" ) );
}
else
{
pointsAdded = true;
}
}

return pointsAdded ? 0 : 2;
}

int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointSequence &ps )
{
if ( !mLayer->isSpatial() )
return 1;

if ( ps.isEmpty() )
{
return 1;
}

bool pointsAdded = false;

QgsPointSequence::const_iterator it = ps.constBegin();
while ( it != ps.constEnd() )
{
if ( addTopologicalPoints( *it ) == 0 )
{
pointsAdded = true;
}
it++;
}

return 0;
return pointsAdded ? 0 : 2;
}

int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
Expand Down

0 comments on commit ee1f3f6

Please sign in to comment.