Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #51437 from YoannQDQ/fix-split-part-tools
Fix split parts tool
  • Loading branch information
troopa81 committed Jan 13, 2023
2 parents 65e965c + 064d077 commit eb168a5
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 30 deletions.
67 changes: 40 additions & 27 deletions src/core/vector/qgsvectorlayereditutils.cpp
Expand Up @@ -486,8 +486,6 @@ Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitParts( const QgsPoin

double xMin, yMin, xMax, yMax;
QgsRectangle bBox; //bounding box of the split line
Qgis::GeometryOperationResult returnCode = Qgis::GeometryOperationResult::Success;
Qgis::GeometryOperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
int numberOfSplitParts = 0;

QgsFeatureIterator fit;
Expand Down Expand Up @@ -542,49 +540,64 @@ Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitParts( const QgsPoin
QgsFeature feat;
while ( fit.nextFeature( feat ) )
{
QVector<QgsGeometry> newGeometries;
QgsPointSequence topologyTestPoints;
QgsGeometry featureGeom = feat.geometry();
splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints, false );

if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success && !newGeometries.isEmpty() )
const QVector<QgsGeometry> geomCollection = featureGeom.asGeometryCollection();
QVector<QgsGeometry> resultCollection;
QgsPointSequence topologyTestPoints;
for ( QgsGeometry part : geomCollection )
{
QgsGeometry newGeom( newGeometries.at( 0 ) );
newGeom.convertToMultiType();
QVector<QgsGeometry> newGeometries;
QgsPointSequence partTopologyTestPoints;

for ( int i = 1; i < newGeometries.size(); ++i )
{
QgsGeometry part = newGeometries.at( i );
part.convertToSingleType();
newGeom.addPart( part );
}
const Qgis::GeometryOperationResult splitFunctionReturn = part.splitGeometry( splitLine, newGeometries, topologicalEditing, partTopologyTestPoints, false );

mLayer->changeGeometry( feat.id(), newGeom );

if ( topologicalEditing )
if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success && !newGeometries.isEmpty() )
{
QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
for ( int i = 0; i < newGeometries.size(); ++i )
{
addTopologicalPoints( *topol_it );
resultCollection.append( newGeometries.at( i ).asGeometryCollection() );
}

topologyTestPoints.append( partTopologyTestPoints );

++numberOfSplitParts;
}
// Note: For multilinestring layers, when the split line does not intersect the feature part,
// QgsGeometry::splitGeometry returns InvalidBaseGeometry instead of NothingHappened
else if ( splitFunctionReturn == Qgis::GeometryOperationResult::NothingHappened ||
splitFunctionReturn == Qgis::GeometryOperationResult::InvalidBaseGeometry )
{
// Add part as is
resultCollection.append( part );
}
else if ( splitFunctionReturn != Qgis::GeometryOperationResult::Success )
{
return splitFunctionReturn;
}
++numberOfSplitParts;
}
else if ( splitFunctionReturn != Qgis::GeometryOperationResult::Success && splitFunctionReturn != Qgis::GeometryOperationResult::NothingHappened )

QgsGeometry newGeom = QgsGeometry::collectGeometry( resultCollection );
mLayer->changeGeometry( feat.id(), newGeom ) ;

if ( topologicalEditing )
{
returnCode = splitFunctionReturn;
QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
{
addTopologicalPoints( *topol_it );
}
}
}

if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 && returnCode == Qgis::GeometryOperationResult::Success )
}
if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 )
{
//There is a selection but no feature has been split.
//Maybe user forgot that only the selected features are split
returnCode = Qgis::GeometryOperationResult::NothingHappened;
return Qgis::GeometryOperationResult::NothingHappened;
}

return returnCode;
return Qgis::GeometryOperationResult::Success;
}


Expand Down
65 changes: 62 additions & 3 deletions tests/src/python/test_qgsvectorlayereditutils.py
Expand Up @@ -40,13 +40,21 @@
start_app()


def createEmptyPolygonLayer():
layer = QgsVectorLayer("Polygon",
"polygon", "memory")
def createEmptyLayer(geomType):
layer = QgsVectorLayer(geomType,
geomType.lower(), "memory")
assert layer.isValid()
return layer


def createEmptyPolygonLayer():
return createEmptyLayer("Polygon")


def createEmptyMultiPolygonLayer():
return createEmptyLayer("MultiPolygon")


class TestQgsVectorLayerEditUtils(unittest.TestCase):

def testAddRing(self):
Expand Down Expand Up @@ -420,6 +428,57 @@ def testMoveVertexPointZM(self):
self.assertEqual(f2.geometry().constGet().z(), 5)
self.assertEqual(f2.geometry().constGet().m(), 6)

def testSplitParts(self):
layer = createEmptyMultiPolygonLayer()
self.assertTrue(layer.startEditing())

pr = layer.dataProvider()

# Add three MultiPolygon features
# Each feature is composed of two squares side by side
# Each feature is on a separate row to form a 3*2 grid
f = QgsFeature(layer.fields(), 1)
f.setGeometry(QgsGeometry.fromWkt('MULTIPOLYGON(((0 0, 4 0, 4 4, 0 4, 0 0)), ((6 0, 10 0, 10 4, 6 4, 6 0)))'))
assert pr.addFeatures([f])

f = QgsFeature(layer.fields(), 2)
f.setGeometry(QgsGeometry.fromWkt('MULTIPOLYGON(((0 6, 4 6, 4 10, 0 10, 0 6)), ((6 6, 10 6, 10 10, 6 10, 6 6)))'))
assert pr.addFeatures([f])

f = QgsFeature(layer.fields(), 3)
f.setGeometry(QgsGeometry.fromWkt('MULTIPOLYGON(((0 12, 4 12, 4 16, 0 16, 0 12)), ((6 12, 10 12, 10 16, 6 16, 6 12)))'))
assert pr.addFeatures([f])

self.assertEqual(layer.featureCount(), 3)

vle = QgsVectorLayerEditUtils(layer)

# Split the first feature with a horizontal line that crosses both its parts
# After this operation, the first feature has 4 parts, the other two are unchanged
result = vle.splitParts([QgsPointXY(0, 2), QgsPointXY(10, 2)], False)
self.assertEqual(result, Qgis.GeometryOperationResult.Success)

# Split all three features with a vertical Line
# After this operation, the first feature has 6 parts, the other two have 6 parts
result = vle.splitParts([QgsPointXY(2, 0), QgsPointXY(2, 16)], False)
self.assertEqual(result, Qgis.GeometryOperationResult.Success)

layer.commitChanges()

self.assertEqual(
layer.getFeature(1).geometry().asWkt(),
'MultiPolygon (((2 0, 2 2, 4 2, 4 0, 2 0)),((2 2, 2 0, 0 0, 0 2, 2 2)),((2 2, 2 4, 4 4, 4 2, 2 2)),((2 4, 2 2, 0 2, 0 4, 2 4)),((6 2, 10 2, 10 0, 6 0, 6 2)),((10 2, 6 2, 6 4, 10 4, 10 2)))'
)
self.assertEqual(
layer.getFeature(2).geometry().asWkt(),
'MultiPolygon (((2 6, 2 10, 4 10, 4 6, 2 6)),((2 10, 2 6, 0 6, 0 10, 2 10)),((6 6, 10 6, 10 10, 6 10, 6 6)))'
)

self.assertEqual(
layer.getFeature(3).geometry().asWkt(),
'MultiPolygon (((2 12, 2 16, 4 16, 4 12, 2 12)),((2 16, 2 12, 0 12, 0 16, 2 16)),((6 12, 10 12, 10 16, 6 16, 6 12)))'
)


if __name__ == '__main__':
unittest.main()

0 comments on commit eb168a5

Please sign in to comment.