Skip to content

Commit ad10b52

Browse files
committed
Fix using add part tool to add part to geometryless rows
(fix #12885, #11319) Also fix some potential crashes with edit tools and null geometry
1 parent 6f860d0 commit ad10b52

File tree

6 files changed

+236
-46
lines changed

6 files changed

+236
-46
lines changed

python/core/geometry/qgsgeometry.sip

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,15 +277,21 @@ class QgsGeometry
277277
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
278278
int addRing( QgsCurveV2* ring );
279279

280-
/** Adds a new island polygon to a multipolygon feature
281-
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
282-
not disjoint with existing polygons of the feature*/
280+
/** Adds a new part to a the geometry.
281+
* @param points points describing part to add
282+
* @param geomType default geometry type to create if no existing geometry
283+
* @returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
284+
* not disjoint with existing polygons of the feature
285+
*/
283286
int addPart( const QList<QgsPoint> &points, QGis::GeometryType geomType = QGis::UnknownGeometry );
284287

285-
/** Adds a new part to this geometry (takes ownership)
286-
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
287-
not disjoint with existing polygons of the feature*/
288-
int addPart( QgsAbstractGeometryV2* part /Transfer/ );
288+
/** Adds a new part to this geometry.
289+
* @param part part to add (ownership is transferred)
290+
* @param geomType default geometry type to create if no existing geometry
291+
* @returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
292+
* not disjoint with existing polygons of the feature
293+
*/
294+
int addPart( QgsAbstractGeometryV2* part /Transfer/, QGis::GeometryType geomType = QGis::UnknownGeometry );
289295

290296
/** Adds a new island polygon to a multipolygon feature
291297
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
@@ -518,6 +524,17 @@ class QgsGeometry
518524
@return true in case of success and false else*/
519525
bool convertToMultiType();
520526

527+
/**
528+
* Converts multi type geometry into single type geometry
529+
* e.g. a multipolygon into a polygon geometry. Only the first part of the
530+
* multi geometry will be retained.
531+
* If it is already a single part geometry, it will return true and
532+
* not change the geometry.
533+
*
534+
* @return true in case of success and false else
535+
*/
536+
bool convertToSingleType();
537+
521538
/** Modifies geometry to avoid intersections with the layers specified in project properties
522539
* @return 0 in case of success,
523540
* 1 if geometry is not of polygon type,

src/core/geometry/qgsgeometry.cpp

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,24 @@ int QgsGeometry::addRing( QgsCurveV2* ring )
590590
}
591591

592592
int QgsGeometry::addPart( const QList<QgsPoint> &points, QGis::GeometryType geomType )
593+
{
594+
QgsAbstractGeometryV2* partGeom = 0;
595+
if ( points.size() == 1 )
596+
{
597+
partGeom = new QgsPointV2( points[0].x(), points[0].y() );
598+
}
599+
else if ( points.size() > 1 )
600+
{
601+
QgsLineStringV2* ringLine = new QgsLineStringV2();
602+
QList< QgsPointV2 > partPoints;
603+
convertPointList( points, partPoints );
604+
ringLine->setPoints( partPoints );
605+
partGeom = ringLine;
606+
}
607+
return addPart( partGeom, geomType );
608+
}
609+
610+
int QgsGeometry::addPart( QgsAbstractGeometryV2* part, QGis::GeometryType geomType )
593611
{
594612
if ( !d )
595613
{
@@ -614,29 +632,13 @@ int QgsGeometry::addPart( const QList<QgsPoint> &points, QGis::GeometryType geom
614632
return 1;
615633
}
616634
}
617-
618-
convertToMultiType();
619-
620-
QgsAbstractGeometryV2* partGeom = 0;
621-
if ( points.size() == 1 )
622-
{
623-
partGeom = new QgsPointV2( points[0].x(), points[0].y() );
624-
}
625-
else if ( points.size() > 1 )
635+
else
626636
{
627-
QgsLineStringV2* ringLine = new QgsLineStringV2();
628-
QList< QgsPointV2 > partPoints;
629-
convertPointList( points, partPoints );
630-
ringLine->setPoints( partPoints );
631-
partGeom = ringLine;
637+
detach( true );
638+
removeWkbGeos();
632639
}
633-
return addPart( partGeom );
634-
}
635640

636-
int QgsGeometry::addPart( QgsAbstractGeometryV2* part )
637-
{
638-
detach( true );
639-
removeWkbGeos();
641+
convertToMultiType();
640642
return QgsGeometryEditUtils::addPart( d->geometry, part );
641643
}
642644

@@ -958,6 +960,30 @@ bool QgsGeometry::convertToMultiType()
958960
return true;
959961
}
960962

963+
bool QgsGeometry::convertToSingleType()
964+
{
965+
if ( !d || !d->geometry )
966+
{
967+
return false;
968+
}
969+
970+
if ( !isMultipart() ) //already single part, no need to convert
971+
{
972+
return true;
973+
}
974+
975+
QgsGeometryCollectionV2* multiGeom = dynamic_cast<QgsGeometryCollectionV2*>( d->geometry );
976+
if ( multiGeom->partCount() < 1 )
977+
return false;
978+
979+
QgsAbstractGeometryV2* firstPart = multiGeom->geometryN( 0 )->clone();
980+
detach( false );
981+
982+
d->geometry = firstPart;
983+
removeWkbGeos();
984+
return true;
985+
}
986+
961987
QgsPoint QgsGeometry::asPoint() const
962988
{
963989
if ( !d || !d->geometry || d->geometry->geometryType() != "Point" )

src/core/geometry/qgsgeometry.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,21 @@ class CORE_EXPORT QgsGeometry
312312
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
313313
int addRing( QgsCurveV2* ring );
314314

315-
/** Adds a new island polygon to a multipolygon feature
316-
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
317-
not disjoint with existing polygons of the feature*/
315+
/** Adds a new part to a the geometry.
316+
* @param points points describing part to add
317+
* @param geomType default geometry type to create if no existing geometry
318+
* @returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
319+
* not disjoint with existing polygons of the feature
320+
*/
318321
int addPart( const QList<QgsPoint> &points, QGis::GeometryType geomType = QGis::UnknownGeometry );
319322

320-
/** Adds a new part to this geometry (takes ownership)
321-
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
322-
not disjoint with existing polygons of the feature*/
323-
int addPart( QgsAbstractGeometryV2* part );
323+
/** Adds a new part to this geometry.
324+
* @param part part to add (ownership is transferred)
325+
* @param geomType default geometry type to create if no existing geometry
326+
* @returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
327+
* not disjoint with existing polygons of the feature
328+
*/
329+
int addPart( QgsAbstractGeometryV2* part, QGis::GeometryType geomType = QGis::UnknownGeometry );
324330

325331
/** Adds a new island polygon to a multipolygon feature
326332
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
@@ -558,6 +564,17 @@ class CORE_EXPORT QgsGeometry
558564
*/
559565
bool convertToMultiType();
560566

567+
/**
568+
* Converts multi type geometry into single type geometry
569+
* e.g. a multipolygon into a polygon geometry. Only the first part of the
570+
* multi geometry will be retained.
571+
* If it is already a single part geometry, it will return true and
572+
* not change the geometry.
573+
*
574+
* @return true in case of success and false else
575+
*/
576+
bool convertToSingleType();
577+
561578
/** Modifies geometry to avoid intersections with the layers specified in project properties
562579
* @return 0 in case of success,
563580
* 1 if geometry is not of polygon type,

src/core/qgsvectorlayereditutils.cpp

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "qgslinestringv2.h"
2121
#include "qgslogger.h"
2222
#include "qgspointv2.h"
23+
#include "qgsgeometryfactory.h"
24+
#include "qgis.h"
25+
#include "qgswkbtypes.h"
2326

2427
#include <limits>
2528

@@ -144,6 +147,9 @@ int QgsVectorLayerEditUtils::addRing( QgsCurveV2* ring, const QgsFeatureIds& tar
144147
//find first valid feature we can add the ring to
145148
while ( fit.nextFeature( f ) )
146149
{
150+
if ( !f.constGeometry() )
151+
continue;
152+
147153
//add ring takes ownership of ring, and deletes it if there's an error
148154
addRingReturnCode = f.geometry()->addRing( static_cast< QgsCurveV2* >( ring->clone() ) );
149155
if ( addRingReturnCode == 0 )
@@ -167,21 +173,34 @@ int QgsVectorLayerEditUtils::addPart( const QList<QgsPoint> &points, QgsFeatureI
167173
return 6;
168174

169175
QgsGeometry geometry;
176+
bool firstPart = false;
170177
if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
171178
{
172179
// it's not in cache: let's fetch it from layer
173180
QgsFeature f;
174-
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
175-
return 6; //geometry not found
181+
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
182+
return 6; //not found
176183

177-
geometry = *f.geometry();
184+
if ( !f.constGeometry() )
185+
{
186+
//no existing geometry, so adding first part to null geometry
187+
firstPart = true;
188+
}
189+
else
190+
{
191+
geometry = *f.geometry();
192+
}
178193
}
179194

180-
geometry.convertToMultiType();
181-
182195
int errorCode = geometry.addPart( points, L->geometryType() );
183196
if ( errorCode == 0 )
184197
{
198+
if ( firstPart && QgsWKBTypes::isSingleType( QGis::fromOldWkbType( L->wkbType() ) )
199+
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
200+
{
201+
//convert back to single part if required by layer
202+
geometry.convertToSingleType();
203+
}
185204
L->editBuffer()->changeGeometry( featureId, &geometry );
186205
}
187206
return errorCode;
@@ -193,21 +212,34 @@ int QgsVectorLayerEditUtils::addPart( QgsCurveV2* ring, QgsFeatureId featureId )
193212
return 6;
194213

195214
QgsGeometry geometry;
215+
bool firstPart = false;
196216
if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
197217
{
198218
// it's not in cache: let's fetch it from layer
199219
QgsFeature f;
200-
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
201-
return 6; //geometry not found
220+
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
221+
return 6; //not found
202222

203-
geometry = *f.geometry();
223+
if ( !f.constGeometry() )
224+
{
225+
//no existing geometry, so adding first part to null geometry
226+
firstPart = true;
227+
}
228+
else
229+
{
230+
geometry = *f.geometry();
231+
}
204232
}
205233

206-
geometry.convertToMultiType();
207-
208-
int errorCode = geometry.addPart( ring );
234+
int errorCode = geometry.addPart( ring, L->geometryType() );
209235
if ( errorCode == 0 )
210236
{
237+
if ( firstPart && QgsWKBTypes::isSingleType( QGis::fromOldWkbType( L->wkbType() ) )
238+
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
239+
{
240+
//convert back to single part if required by layer
241+
geometry.convertToSingleType();
242+
}
211243
L->editBuffer()->changeGeometry( featureId, &geometry );
212244
}
213245
return errorCode;

src/core/qgsvectorlayerundocommand.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ QgsVectorLayerUndoCommandChangeGeometry::QgsVectorLayerUndoCommandChangeGeometry
119119
{
120120
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.find( mFid );
121121
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
122-
mOldGeom = new QgsGeometry( *it.value().constGeometry() );
122+
mOldGeom = ( it.value().constGeometry() ? new QgsGeometry( *it.value().constGeometry() ) : 0 );
123123
}
124124
else
125125
{

0 commit comments

Comments
 (0)