From 5b1678ef211fbac9593e91a1a4b4329cc399a5c3 Mon Sep 17 00:00:00 2001 From: uclaros Date: Fri, 15 May 2026 15:59:06 +0300 Subject: [PATCH] Allow adding a new part to multipart geometries --- app/maptools/recordingmaptool.cpp | 30 ++ app/maptools/recordingmaptool.h | 7 + app/qml/map/MMMapController.qml | 48 +++- app/test/testmaptools.cpp | 448 ++++++++++++++++++++++++++++++ app/test/testmaptools.h | 6 + 5 files changed, 536 insertions(+), 3 deletions(-) diff --git a/app/maptools/recordingmaptool.cpp b/app/maptools/recordingmaptool.cpp index d4e98e9bb..ad10726c0 100644 --- a/app/maptools/recordingmaptool.cpp +++ b/app/maptools/recordingmaptool.cpp @@ -1150,6 +1150,36 @@ void RecordingMapTool::cancelGrab() setActiveVertex( Vertex() ); } +void RecordingMapTool::startDigitizingNewPart() +{ + // if maptool is in GRAB and VIEW state, no part should be added + if ( mState == RecordingMapTool::View || mState == RecordingMapTool::Grab ) + { + return; + } + + QgsAbstractGeometry *geom = mRecordedGeometry.get(); + if ( QgsGeometryCollection *collection = qgsgeometry_cast( geom ) ) + { + switch ( mRecordedGeometry.type() ) + { + case Qgis::GeometryType::Line: + collection->addGeometry( new QgsLineString() ); + setActivePartAndRing( collection->partCount() - 1, 0 ); + break; + case Qgis::GeometryType::Polygon: + collection->addGeometry( new QgsPolygon( new QgsLineString(), QList() ) ); + setActivePartAndRing( collection->partCount() - 1, 0 ); + break; + case Qgis::GeometryType::Point: + // MultiPoints do not need an empty placeholder part, new point part is directly appended when digitizing + case Qgis::GeometryType::Unknown: + case Qgis::GeometryType::Null: + break; + } + } +} + double RecordingMapTool::pixelsToMapUnits( double numPixels ) { QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings()->mapSettings() ); diff --git a/app/maptools/recordingmaptool.h b/app/maptools/recordingmaptool.h index 0587fde9c..ce85210d6 100644 --- a/app/maptools/recordingmaptool.h +++ b/app/maptools/recordingmaptool.h @@ -184,6 +184,13 @@ class RecordingMapTool : public AbstractMapTool Q_INVOKABLE void cancelGrab(); + /** + * When this is called on a multipart geometry, a new empty part will be added to the geometry and + * activePart will be set to that new last part. + * For MultiPoints and singlepart geometries no new empty part will be added. + */ + Q_INVOKABLE void startDigitizingNewPart(); + // Getters / setters bool centeredToGPS() const; void setCenteredToGPS( bool newCenteredToGPS ); diff --git a/app/qml/map/MMMapController.qml b/app/qml/map/MMMapController.qml index f3b3601ee..4c1b8c436 100644 --- a/app/qml/map/MMMapController.qml +++ b/app/qml/map/MMMapController.qml @@ -814,17 +814,22 @@ Item { return "invisible" if (internal.splitGeometryButtonVisible) { - if (!internal.redrawGeometryButtonVisible && !internal.streamingModeButtonVisible) + if (!internal.redrawGeometryButtonVisible && !internal.streamingModeButtonVisible && !internal.addPartButtonVisible) return "split" } + if (internal.addPartButtonVisible) { + if (!internal.splitGeometryButtonVisible && !internal.streamingModeButtonVisible && !internal.redrawGeometryButtonVisible) + return "addPart" + } + if (internal.redrawGeometryButtonVisible) { - if (!internal.splitGeometryButtonVisible && !internal.streamingModeButtonVisible) + if (!internal.splitGeometryButtonVisible && !internal.streamingModeButtonVisible && !internal.addPartButtonVisible) return "redraw" } if (internal.streamingModeButtonVisible) { - if (!internal.redrawGeometryButtonVisible && !internal.splitGeometryButtonVisible) + if (!internal.redrawGeometryButtonVisible && !internal.splitGeometryButtonVisible && !internal.addPartButtonVisible) return "stream" } return "menu" @@ -834,6 +839,9 @@ Item { if (actionState === "split") return __style.splitGeometryIcon + if (actionState === "addPart") + return __style.plusIcon + if (actionState === "redraw") return __style.redrawGeometryIcon @@ -847,6 +855,9 @@ Item { if (actionState === "split") return root.toggleSplitting() + if (actionState === "addPart") + return root.toggleAddPart() + if (actionState === "redraw") return root.toggleRedraw() @@ -977,6 +988,18 @@ Item { } } + MMListDelegate { + text: qsTr( "Add part" ) + leftContent: MMIcon { source: __style.plusIcon } + + visible: internal.addPartButtonVisible + + onClicked: { + root.toggleAddPart() + moreToolsMenu.close() + } + } + MMListDelegate { text: qsTr( "Redraw geometry" ) leftContent: MMIcon { source: __style.redrawGeometryIcon } @@ -1262,6 +1285,7 @@ Item { // visibility of buttons in "more" menu property bool splitGeometryButtonVisible: !internal.isPointLayer && !root.isStreaming && root.state === "edit" + property bool addPartButtonVisible: internal.isMultiPartLayer && !root.isStreaming && root.state === "edit" property bool redrawGeometryButtonVisible: root.state === "edit" property bool streamingModeButtonVisible: !internal.isPointLayer || internal.isMultiPartLayer @@ -1323,6 +1347,24 @@ Item { } } + function toggleAddPart() { + addPart( internal.featurePairToEdit ) + } + + function addPart( featurepair) { + __activeProject.setActiveLayer( featurepair.layer ) + root.centerToPair( featurepair ) + root.showInfoTextMessage( qsTr( "Add new part to the geometry" ) ) + + internal.featurePairToEdit = featurepair + + // You should be already in state == "edit" + if ( recordingToolsLoader.active ) { + recordingToolsLoader.item.recordingMapTool.state = MM.RecordingMapTool.Record + recordingToolsLoader.item.recordingMapTool.startDigitizingNewPart() + } + } + function toggleSplitting() { split(internal.featurePairToEdit) } diff --git a/app/test/testmaptools.cpp b/app/test/testmaptools.cpp index d714938a4..bd40183c8 100644 --- a/app/test/testmaptools.cpp +++ b/app/test/testmaptools.cpp @@ -17,6 +17,7 @@ #include "qgslinestring.h" #include "qgspolygon.h" #include "qgsmultipolygon.h" +#include "qgsmultilinestring.h" #include "qgslinestring.h" #include "qgsgeometry.h" @@ -1445,6 +1446,453 @@ void TestMapTools::testAddVertexMultiPolygonLayer() delete polygonLayer; } +void TestMapTools::testAddPartPointLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QCOMPARE( mapTool.recordingType(), RecordingMapTool::Manual ); + + // Create memory layer to work with + QgsVectorLayer *pointLayer = new QgsVectorLayer( "Point?crs=epsg:4326", "pointlayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + + mapTool.setActiveLayer( pointLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + QVector pointsToAdd = + { + { -97.129, 22.602 }, + { -104.923, 24.840 } + }; + + // + // Point layer should only add point when geometry is empty + // + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + mapTool.addPoint( pointsToAdd[0] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 1 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 0 ), pointsToAdd[0] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + // startDigitizingNewPart should not affect singlepart layers + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 1 ); + + // geometry did not change + QVERIFY( mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 1 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 0 ), pointsToAdd[0] ); + + QCOMPARE( mapTool.activePart(), 0 ); + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + QVERIFY( mapTool.recordedGeometry().wkbType() == Qgis::WkbType::Point ); + + delete project; + delete pointLayer; +} + +void TestMapTools::testAddPartMultiPointLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QgsVectorLayer *multiPointLayer = new QgsVectorLayer( "MultiPoint?crs=epsg:4326", "mpointlayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + mapTool.setActiveLayer( multiPointLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + QVector pointsToAdd = + { + { -97.129, 22.602 }, + { -104.923, 24.840 }, + { -108.0, 26.0 }, + }; + + mapTool.addPoint( pointsToAdd[0] ); + mapTool.addPoint( pointsToAdd[1] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 2 ); + + // startDigitizingNewPart on MultiPoint does not add an empty placeholder part + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 2 ); + QCOMPARE( mapTool.activePart(), 0 ); + + // adding another point still appends a new part + mapTool.addPoint( pointsToAdd[2] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 3 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 3 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 2 ), pointsToAdd[2] ); + QCOMPARE( mapTool.activePart(), 0 ); + + delete project; + delete multiPointLayer; +} + +void TestMapTools::testAddPartLineLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QCOMPARE( mapTool.recordingType(), RecordingMapTool::Manual ); + + // Create memory layer to work with + QgsVectorLayer *lineLayer = new QgsVectorLayer( "LineString?crs=epsg:4326", "linelayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + + mapTool.setActiveLayer( lineLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + // + // ----------- Linestring layer ---------- + // + QVector pointsToAdd = + { + { -97.129, 22.602 }, // added to end + { -104.923, 24.840 }, // added to end + { -108, 26 }, // added to end + { -110, 28 }, // added to end + { -110, 28 }, // Same as previous point should not be recorded + { -95, 20 }, // added to start + { -109, 27 }, // added to middle + }; + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + mapTool.addPoint( pointsToAdd[0] ); + + QVERIFY( !mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 1 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 0 ), pointsToAdd[0] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + mapTool.addPoint( pointsToAdd[1] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 2 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 1 ), pointsToAdd[1] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + QVERIFY( mapTool.recordedGeometry().wkbType() == Qgis::WkbType::LineString ); + + // startDigitizingNewPart should not affect singlepart layers + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 1 ); + QCOMPARE( mapTool.activePart(), 0 ); + + mapTool.addPoint( pointsToAdd[2] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 3 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 2 ), pointsToAdd[2] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + delete project; + delete lineLayer; +} + +void TestMapTools::testAddPartMultiLineLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QgsVectorLayer *multiLineLayer = new QgsVectorLayer( "MultiLineString?crs=epsg:4326", "mlinelayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + mapTool.setActiveLayer( multiLineLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + QVector pointsToAdd = + { + { -97.129, 22.602 }, + { -104.923, 24.840 }, + { -108.0, 26.0 }, + { -80.0, 30.0 }, + { -85.0, 35.0 }, + }; + + // build first line part + mapTool.addPoint( pointsToAdd[0] ); + mapTool.addPoint( pointsToAdd[1] ); + mapTool.addPoint( pointsToAdd[2] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 1 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 3 ); + QCOMPARE( mapTool.activePart(), 0 ); + + // start digitizing a new part + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 3 ); + QCOMPARE( mapTool.activePart(), 1 ); + + // new points are added to the new part + mapTool.addPoint( pointsToAdd[3] ); + + // second part has only 1 vertex so the overall geometry is not yet valid + QVERIFY( !mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 4 ); + + mapTool.addPoint( pointsToAdd[4] ); + + // second part now has 2 vertices — both parts are valid + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 5 ); + + const QgsMultiLineString *mls = qgsgeometry_cast( mapTool.recordedGeometry().constGet() ); + QVERIFY( mls ); + QCOMPARE( mls->lineStringN( 0 )->numPoints(), 3 ); + QCOMPARE( mls->lineStringN( 1 )->numPoints(), 2 ); + QCOMPARE( mls->lineStringN( 0 )->pointN( 0 ), pointsToAdd[0] ); + QCOMPARE( mls->lineStringN( 0 )->pointN( 2 ), pointsToAdd[2] ); + QCOMPARE( mls->lineStringN( 1 )->pointN( 0 ), pointsToAdd[3] ); + QCOMPARE( mls->lineStringN( 1 )->pointN( 1 ), pointsToAdd[4] ); + + // nothing should be added in VIEW (neither in GRAB) state + mapTool.setState( RecordingMapTool::View ); + + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.activePart(), 1 ); + + mapTool.setState( RecordingMapTool::Grab ); + + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.activePart(), 1 ); + + delete project; + delete multiLineLayer; +} + +void TestMapTools::testAddPartPolygonLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QCOMPARE( mapTool.recordingType(), RecordingMapTool::Manual ); + + // Create memory layer to work with + QgsVectorLayer *polygonLayer = new QgsVectorLayer( "Polygon?crs=epsg:4326", "polygonlayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + mapTool.setActiveLayer( polygonLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + // + // ----------- Polygon layer ---------- + // + QVector pointsToAdd = + { + { -95.5, 22.0 }, + { -97.5, 22.0 }, + { -97.5, 26.0 }, + { -95.5, 26.0 }, + { -96.5, 22.0 }, // add between first two + { -97.5, 24.0 }, // add between second & third + { -95.5, 24.0 }, // add between third & last + }; + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + mapTool.addPoint( pointsToAdd[0] ); + + QVERIFY( !mapTool.hasValidGeometry() ); + + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 1 ); + QCOMPARE( mapTool.recordedGeometry().constGet()->nCoordinates(), 1 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 0 ), pointsToAdd[0] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + mapTool.addPoint( pointsToAdd[1] ); + + QVERIFY( !mapTool.hasValidGeometry() ); + // ring will be closed, hance 3 points + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 3 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 1 ), pointsToAdd[1] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + QVERIFY( mapTool.recordedGeometry().wkbType() == Qgis::WkbType::Polygon ); + + mapTool.addPoint( pointsToAdd[2] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QVERIFY( mapTool.recordedGeometry().constGet()->nCoordinates() == 4 ); + QCOMPARE( mapTool.recordedGeometry().vertexAt( 2 ), pointsToAdd[2] ); + + QVERIFY( !mapTool.activeVertex().isValid() ); + QVERIFY( mapTool.state() == RecordingMapTool::Record ); + + // startDigitizingNewPart should not affect singlepart layers + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 1 ); + QCOMPARE( mapTool.activePart(), 0 ); + + delete project; + delete polygonLayer; +} + +void TestMapTools::testAddPartMultiPolygonLayer() +{ + RecordingMapTool mapTool; + + QgsProject *project = TestUtils::loadPlanesTestProject(); + QVERIFY( project && !project->homePath().isEmpty() ); + + InputMapCanvasMap canvas; + InputMapSettings *ms = canvas.mapSettings(); + setupMapSettings( ms, project, QgsRectangle( -107.54331499504026226, 21.62302175066136556, -72.73224633912816728, 51.49933451998575151 ), QSize( 600, 1096 ) ); + + mapTool.setMapSettings( ms ); + + QgsVectorLayer *multiPolygonLayer = new QgsVectorLayer( "MultiPolygon?crs=epsg:4326", "mpolylayer", "memory" ); + + mapTool.setState( RecordingMapTool::Record ); + mapTool.setActiveLayer( multiPolygonLayer ); + mapTool.setActiveFeature( QgsFeature() ); + + QVector pointsToAdd = + { + { -95.5, 22.0 }, + { -97.5, 22.0 }, + { -97.5, 26.0 }, + { -80.0, 30.0 }, + { -82.0, 30.0 }, + { -82.0, 34.0 }, + }; + + // build first polygon part (3 unique vertices make a valid polygon) + mapTool.addPoint( pointsToAdd[0] ); + mapTool.addPoint( pointsToAdd[1] ); + mapTool.addPoint( pointsToAdd[2] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 1 ); + QCOMPARE( mapTool.activePart(), 0 ); + QCOMPARE( mapTool.recordedGeometry().wkbType(), Qgis::WkbType::MultiPolygon ); + + // start digitizing a new part + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.activePart(), 1 ); + + // new points are added to the new polygon part + mapTool.addPoint( pointsToAdd[3] ); + mapTool.addPoint( pointsToAdd[4] ); + mapTool.addPoint( pointsToAdd[5] ); + + QVERIFY( mapTool.hasValidGeometry() ); + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + + const QgsMultiPolygon *mp = qgsgeometry_cast( mapTool.recordedGeometry().constGet() ); + QVERIFY( mp ); + // each polygon exterior ring has 4 vertices (3 unique + closing vertex) + QCOMPARE( mp->polygonN( 0 )->exteriorRing()->numPoints(), 4 ); + QCOMPARE( mp->polygonN( 1 )->exteriorRing()->numPoints(), 4 ); + const QgsLineString *ring1 = qgsgeometry_cast( mp->polygonN( 1 )->exteriorRing() ); + QVERIFY( ring1 ); + QCOMPARE( ring1->pointN( 0 ), pointsToAdd[3] ); + QCOMPARE( ring1->pointN( 1 ), pointsToAdd[4] ); + QCOMPARE( ring1->pointN( 2 ), pointsToAdd[5] ); + + // nothing should be added in VIEW (neither in GRAB) state + mapTool.setState( RecordingMapTool::View ); + + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.activePart(), 1 ); + + mapTool.setState( RecordingMapTool::Grab ); + + mapTool.startDigitizingNewPart(); + + QCOMPARE( mapTool.recordedGeometry().constGet()->partCount(), 2 ); + QCOMPARE( mapTool.activePart(), 1 ); + + delete project; + delete multiPolygonLayer; +} + void TestMapTools::testUpdateVertex() { RecordingMapTool mapTool; diff --git a/app/test/testmaptools.h b/app/test/testmaptools.h index f2d37c1dc..e570d0620 100644 --- a/app/test/testmaptools.h +++ b/app/test/testmaptools.h @@ -44,6 +44,12 @@ class TestMapTools : public QObject void testAddVertexMultiLineLayer(); void testAddVertexPolygonLayer(); void testAddVertexMultiPolygonLayer(); + void testAddPartPointLayer(); + void testAddPartLineLayer(); + void testAddPartPolygonLayer(); + void testAddPartMultiPointLayer(); + void testAddPartMultiLineLayer(); + void testAddPartMultiPolygonLayer(); void testUpdateVertex(); void testRemoveVertex(); void testVerticesStructure();