Skip to content

Commit c686c4f

Browse files
committed
geometry: allow removing parts and rings or geometries by removing all vertices
(fixes #10684)
1 parent d7bd9c7 commit c686c4f

File tree

5 files changed

+115
-13
lines changed

5 files changed

+115
-13
lines changed

src/app/nodetool/qgsselectedfeature.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ void QgsSelectedFeature::deleteSelectedVertexes()
258258
beginGeometryChange();
259259

260260
int count = 0;
261-
for ( int i = mVertexMap.size() - 1; i > -1; i-- )
261+
for ( int i = mVertexMap.size() - 1; i > -1 && nSelected > 0; i-- )
262262
{
263263
if ( mVertexMap[i]->isSelected() )
264264
{

src/core/qgsgeometry.cpp

+87-10
Original file line numberDiff line numberDiff line change
@@ -1363,18 +1363,31 @@ bool QgsGeometry::moveVertex( double x, double y, int atVertex )
13631363
}
13641364
}
13651365

1366-
bool QgsGeometry::deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int atVertex, bool hasZValue, int &pointIndex, bool isRing, bool lastItem )
1366+
// copy vertices from srcPtr to dstPtr and skip/delete one vertex
1367+
// @param srcPtr ring/part starting with number of points (adjusted in each call)
1368+
// @param dstPtr ring/part to copy to (adjusted in each call)
1369+
// @param atVertex index of vertex to skip
1370+
// @param hasZValue points have 3 elements
1371+
// @param pointIndex reference to index of first ring/part vertex in overall object (adjusted in each call)
1372+
// @param isRing srcPtr points to a ring
1373+
// @param lastItem last ring/part, atVertex after this one must be wrong
1374+
// @return
1375+
// 0 no delete was done
1376+
// 1 "normal" delete was done
1377+
// 2 last element of the ring/part was deleted
1378+
int QgsGeometry::deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int atVertex, bool hasZValue, int &pointIndex, bool isRing, bool lastItem )
13671379
{
13681380
QgsDebugMsg( QString( "atVertex:%1 hasZValue:%2 pointIndex:%3 isRing:%4" ).arg( atVertex ).arg( hasZValue ).arg( pointIndex ).arg( isRing ) );
13691381
const int ps = ( hasZValue ? 3 : 2 ) * sizeof( double );
13701382
int nPoints;
13711383
srcPtr >> nPoints;
13721384

1385+
// copy complete ring/part if vertex is in a following one
13731386
if ( atVertex < pointIndex || atVertex >= pointIndex + nPoints )
13741387
{
13751388
// atVertex does not exist
13761389
if ( lastItem && atVertex >= pointIndex + nPoints )
1377-
return false;
1390+
return 0;
13781391

13791392
dstPtr << nPoints;
13801393

@@ -1383,14 +1396,25 @@ bool QgsGeometry::deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int a
13831396
dstPtr += len;
13841397
srcPtr += len;
13851398
pointIndex += nPoints;
1386-
return false;
1399+
return 0;
13871400
}
13881401

1402+
// delete the first vertex of a ring instead of the last
13891403
if ( isRing && atVertex == pointIndex + nPoints - 1 )
13901404
atVertex = pointIndex;
13911405

1406+
if ( nPoints == ( isRing ? 2 : 1 ) )
1407+
{
1408+
// last point of the part/ring is deleted
1409+
// skip the whole part/ring
1410+
srcPtr += nPoints * ps;
1411+
pointIndex += nPoints;
1412+
return 2;
1413+
}
1414+
13921415
dstPtr << nPoints - 1;
13931416

1417+
// copy ring before vertex
13941418
int len = ( atVertex - pointIndex ) * ps;
13951419
if ( len > 0 )
13961420
{
@@ -1399,9 +1423,13 @@ bool QgsGeometry::deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int a
13991423
srcPtr += len;
14001424
}
14011425

1426+
// skip deleted vertex
14021427
srcPtr += ps;
14031428

1429+
// copy reset of ring
14041430
len = ( pointIndex + nPoints - atVertex - 1 ) * ps;
1431+
1432+
// save position of vertex, if we delete the first vertex of a ring
14051433
const unsigned char *first = 0;
14061434
if ( isRing && atVertex == pointIndex )
14071435
{
@@ -1416,20 +1444,22 @@ bool QgsGeometry::deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int a
14161444
srcPtr += len;
14171445
}
14181446

1447+
// copy new first vertex instead of the old last, if we deleted the original first vertex
14191448
if ( first )
14201449
{
14211450
memcpy( dstPtr, first, ps );
14221451
dstPtr += ps;
14231452
srcPtr += ps;
14241453
}
14251454

1426-
pointIndex += nPoints - 1;
1455+
pointIndex += nPoints;
14271456

1428-
return true;
1457+
return 1;
14291458
}
14301459

14311460
bool QgsGeometry::deleteVertex( int atVertex )
14321461
{
1462+
QgsDebugMsg( QString( "atVertex:%1" ).arg( atVertex ) );
14331463
if ( atVertex < 0 )
14341464
return false;
14351465

@@ -1468,7 +1498,14 @@ bool QgsGeometry::deleteVertex( int atVertex )
14681498
case QGis::WKBLineString:
14691499
{
14701500
int pointIndex = 0;
1471-
deleted = deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, false, true );
1501+
int res = deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, false, true );
1502+
if ( res == 2 )
1503+
{
1504+
// Linestring with 0 points
1505+
dstPtr << 0;
1506+
}
1507+
1508+
deleted = res != 0;
14721509
break;
14731510
}
14741511

@@ -1477,10 +1514,17 @@ bool QgsGeometry::deleteVertex( int atVertex )
14771514
{
14781515
int nRings;
14791516
srcPtr >> nRings;
1517+
QgsWkbPtr ptrN( dstPtr );
14801518
dstPtr << nRings;
14811519

14821520
for ( int ringnr = 0, pointIndex = 0; ringnr < nRings; ++ringnr )
1483-
deleted |= deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, true, ringnr == nRings - 1 );
1521+
{
1522+
int res = deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, true, ringnr == nRings - 1 );
1523+
if ( res == 2 )
1524+
ptrN << nRings - 1;
1525+
1526+
deleted |= res != 0;
1527+
}
14841528

14851529
break;
14861530
}
@@ -1524,13 +1568,24 @@ bool QgsGeometry::deleteVertex( int atVertex )
15241568
{
15251569
int nLines;
15261570
srcPtr >> nLines;
1571+
QgsWkbPtr ptrN( dstPtr );
15271572
dstPtr << nLines;
15281573

15291574
for ( int linenr = 0, pointIndex = 0; linenr < nLines; ++linenr )
15301575
{
1576+
QgsWkbPtr saveDstPtr( dstPtr );
15311577
srcPtr >> endianness >> wkbType;
15321578
dstPtr << endianness << wkbType;
1533-
deleted |= deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, false, linenr == nLines - 1 );
1579+
1580+
int res = deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, false, linenr == nLines - 1 );
1581+
if ( res == 2 )
1582+
{
1583+
// line string was completely removed
1584+
ptrN << nLines - 1;
1585+
dstPtr = saveDstPtr;
1586+
}
1587+
1588+
deleted |= res != 0;
15341589
}
15351590

15361591
break;
@@ -1541,16 +1596,38 @@ bool QgsGeometry::deleteVertex( int atVertex )
15411596
{
15421597
int nPolys;
15431598
srcPtr >> nPolys;
1599+
QgsWkbPtr ptrNPolys( dstPtr );
15441600
dstPtr << nPolys;
15451601

15461602
for ( int polynr = 0, pointIndex = 0; polynr < nPolys; ++polynr )
15471603
{
15481604
int nRings;
15491605
srcPtr >> endianness >> wkbType >> nRings;
1550-
dstPtr << endianness << wkbType << nRings;
1606+
QgsWkbPtr saveDstPolyPtr( dstPtr );
1607+
dstPtr << endianness << wkbType;
1608+
QgsWkbPtr ptrNRings( dstPtr );
1609+
dstPtr << nRings;
15511610

15521611
for ( int ringnr = 0; ringnr < nRings; ++ringnr )
1553-
deleted |= deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, true, polynr == nPolys - 1 && ringnr == nRings - 1 );
1612+
{
1613+
int res = deleteVertex( srcPtr, dstPtr, atVertex, hasZValue, pointIndex, true, polynr == nPolys - 1 && ringnr == nRings - 1 );
1614+
if ( res == 2 )
1615+
{
1616+
// ring was completely removed
1617+
if ( nRings == 1 )
1618+
{
1619+
// last ring => remove polygon
1620+
ptrNPolys << nPolys - 1;
1621+
dstPtr = saveDstPolyPtr;
1622+
}
1623+
else
1624+
{
1625+
ptrNRings << nRings - 1;
1626+
}
1627+
}
1628+
1629+
deleted |= res != 0;
1630+
}
15541631
}
15551632
break;
15561633
}

src/core/qgsgeometry.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ class CORE_EXPORT QgsGeometry
648648
double leftOf( double x, double y, double& x1, double& y1, double& x2, double& y2 );
649649

650650
static inline bool moveVertex( QgsWkbPtr &wkbPtr, const double &x, const double &y, int atVertex, bool hasZValue, int &pointIndex, bool isRing );
651-
static inline bool deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int atVertex, bool hasZValue, int &pointIndex, bool isRing, bool lastItem );
651+
static inline int deleteVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int atVertex, bool hasZValue, int &pointIndex, bool isRing, bool lastItem );
652652
static inline bool insertVertex( QgsConstWkbPtr &srcPtr, QgsWkbPtr &dstPtr, int beforeVertex, const double &x, const double &y, bool hasZValue, int &pointIndex, bool isRing );
653653

654654
/** try to convert the geometry to a point */

tests/src/python/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ADD_PYTHON_TEST(PyQgsLocalServer test_qgis_local_server.py)
44
ADD_PYTHON_TEST(PyQgsFontUtils test_qgsfontutils.py)
55
ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
66
ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py)
7-
ADD_PYTHON_TEST(PyQgsGeometry test_qgsgeometry.py)
7+
ADD_PYTHON_TEST(PyQgsGeometryTest test_qgsgeometry.py)
88
ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersections.py)
99
ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
1010
ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)

tests/src/python/test_qgsgeometry.py

+25
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,31 @@ def testDeleteVertex(self):
950950
wkt = polygon.exportToWkt()
951951
assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt )
952952

953+
# 3-+-+-+-+-+-+-+-+-2
954+
# | |
955+
# + 8-7 3-2 8-7 3-2 +
956+
# | | | | | | | | | |
957+
# + 5-6 0-1 5-6 0-1 +
958+
# | |
959+
# 0-+-+-+-+---+-+-+-1
960+
polygon = QgsGeometry.fromWkt( "POLYGON((0 0,9 0,9 3,0 3,0 0),(1 1,2 1,2 2,1 2,1 1),(3 1,4 1,4 2,3 2,3 1),(5 1,6 1,6 2,5 2,5 1),(7 1,8 1,8 2,7 2,7 1))" )
961+
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
962+
963+
for i in range(4):
964+
assert polygon.deleteVertex(16), "Delete vertex 16 failed" % i
965+
966+
expwkt = "POLYGON((0 0,9 0,9 3,0 3,0 0),(1 1,2 1,2 2,1 2,1 1),(3 1,4 1,4 2,3 2,3 1),(7 1,8 1,8 2,7 2,7 1))"
967+
wkt = polygon.exportToWkt()
968+
assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt )
969+
970+
for i in range(3):
971+
for j in range(4):
972+
assert polygon.deleteVertex(5), "Delete vertex 5 failed" % i
973+
974+
expwkt = "POLYGON((0 0,9 0,9 3,0 3,0 0))"
975+
wkt = polygon.exportToWkt()
976+
assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt )
977+
953978
def testInsertVertex(self):
954979
linestring = QgsGeometry.fromWkt( "LINESTRING(1 0,2 0)" )
955980

0 commit comments

Comments
 (0)