Skip to content

Commit d3d8f4d

Browse files
authored
Merge pull request #5223 from boundlessgeo/geom_compatibility_check_release-2_18-fix15741
On behalf of Giovanni: Geom compatibility check release 2.18 fixes #15741 #16927
2 parents 03a444c + 278a88c commit d3d8f4d

20 files changed

+343
-118
lines changed

python/core/qgsvectordataprovider.sip

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,17 @@ class QgsVectorDataProvider : QgsDataProvider
390390

391391
void pushError( const QString& msg );
392392

393-
/** Converts the geometry to the provider type if possible / necessary
394-
@return the converted geometry or nullptr if no conversion was necessary or possible*/
393+
/** \brief Converts the geometry to the provider type if possible / necessary
394+
* this is the list of possible modifications:
395+
* - convert compoundcurve to circularstring
396+
* (possible if compoundcurve consists of one circular string)
397+
* - convert to multitype if necessary
398+
* - convert to curved type if necessary
399+
* - convert to linear type from curved type
400+
* - Add z/m 0 default values
401+
* - Remove z/m
402+
* \ref QgsVectorLayerEditBuffer::adaptGeometry()
403+
* \param geom Geometry to convert
404+
* \returns the converted geometry or nullptr if no conversion was necessary or possible*/
395405
QgsGeometry* convertToProviderType( const QgsGeometry* geom ) const /Factory/;
396406
};

python/core/qgsvectorlayereditbuffer.sip

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,17 @@ class QgsVectorLayerEditBuffer : QObject
148148
void updateAttributeMapIndex( QgsAttributeMap& attrs, int index, int offset ) const;
149149

150150
void updateLayerFields();
151+
152+
/** \brief Apply geometry modification basing on provider geometry type.
153+
* Geometry is modified only if successful conversion is possible.
154+
* adaptGeometry calls \ref QgsVectorDataProvider::convertToProviderType()
155+
* if necessary and that apply the modifications.
156+
* \param geometry pointer to the geometry that should be adapted to provider
157+
* \returns bool true if success.
158+
* True: Input geometry is changed because conversion is applied or
159+
* geometry is untouched if geometry conversion is not necessary.
160+
* False: Conversion is not possible and geometry is untouched.
161+
* \note added in QGIS 2.18.14
162+
*/
163+
bool adaptGeometry( QgsGeometry* geometry );
151164
};

src/app/qgisapp.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7162,7 +7162,7 @@ void QgisApp::mergeSelectedFeatures()
71627162
if ( !isDefaultValue && !vl->fields().at( i ).convertCompatible( val ) )
71637163
{
71647164
messageBar()->pushMessage(
7165-
tr( "Invalid result" ),
7165+
tr( "Merge features" ),
71667166
tr( "Could not store value '%1' in field of type %2" ).arg( attrs.at( i ).toString(), vl->fields().at( i ).typeName() ),
71677167
QgsMessageBar::WARNING );
71687168
}
@@ -7176,9 +7176,15 @@ void QgisApp::mergeSelectedFeatures()
71767176
vl->deleteFeature( *feature_it );
71777177
}
71787178

7179-
vl->addFeature( newFeature, false );
7180-
7181-
vl->endEditCommand();
7179+
// addFeature can fail if newFeature has no compatibile geometry
7180+
if ( !vl->addFeature( newFeature, false ) )
7181+
{
7182+
vl->destroyEditCommand();
7183+
}
7184+
else
7185+
{
7186+
vl->endEditCommand();
7187+
}
71827188

71837189
if ( mapCanvas() )
71847190
{
@@ -7533,8 +7539,14 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
75337539
++featureIt;
75347540
}
75357541

7536-
pasteVectorLayer->addFeatures( features );
7537-
pasteVectorLayer->endEditCommand();
7542+
if ( !pasteVectorLayer->addFeatures( features ) )
7543+
{
7544+
pasteVectorLayer->destroyEditCommand();
7545+
}
7546+
else
7547+
{
7548+
pasteVectorLayer->endEditCommand();
7549+
}
75387550

75397551
int nCopiedFeatures = features.count();
75407552
if ( nCopiedFeatures == 0 )
@@ -7721,7 +7733,7 @@ QgsVectorLayer *QgisApp::pasteToNewMemoryVector()
77217733
feature.geometry()->convertToMultiType();
77227734
}
77237735
}
7724-
if ( ! layer->addFeatures( features, false ) || !layer->commitChanges() )
7736+
if ( !layer->addFeatures( features, false ) || !layer->commitChanges() )
77257737
{
77267738
QgsDebugMsg( "Cannot add features or commit changes" );
77277739
delete layer;

src/app/qgsfieldcalculator.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,12 @@ void QgsFieldCalculator::accept()
295295
if ( value.canConvert< QgsGeometry >() )
296296
{
297297
QgsGeometry geom = value.value< QgsGeometry >();
298-
mVectorLayer->changeGeometry( feature.id(), &geom );
298+
if ( !mVectorLayer->changeGeometry( feature.id(), &geom ) )
299+
{
300+
calculationSuccess = false;
301+
error = tr( "Can not change geometry for feature: %1", "Field calculator" ).arg( feature.id() );
302+
break;
303+
}
299304
}
300305
}
301306
else

src/app/qgsmaptooladdpart.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
219219
case 6:
220220
errorMessage = tr( "Selected geometry could not be found" );
221221
break;
222+
223+
case 7:
224+
errorMessage = tr( "Update geometry error" );
225+
break;
222226
}
223227

224228
emit messageEmitted( errorMessage, QgsMessageBar::WARNING );

src/app/qgsmaptooldeletepart.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ void QgsMapToolDeletePart::canvasReleaseEvent( QgsMapMouseEvent* e )
105105
if ( g->deletePart( mPressedPartNum ) )
106106
{
107107
vlayer->beginEditCommand( tr( "Part of multipart feature deleted" ) );
108-
vlayer->changeGeometry( f.id(), g );
109-
vlayer->endEditCommand();
108+
if ( !vlayer->changeGeometry( f.id(), g ) )
109+
vlayer->destroyEditCommand();
110+
else
111+
vlayer->endEditCommand();
110112
vlayer->triggerRepaint();
111113
}
112114
else

src/app/qgsmaptooldeletering.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ void QgsMapToolDeleteRing::canvasReleaseEvent( QgsMapMouseEvent* e )
113113
if ( g->deleteRing( mPressedRingNum, mPressedPartNum ) )
114114
{
115115
vlayer->beginEditCommand( tr( "Ring deleted" ) );
116-
vlayer->changeGeometry( mPressedFid, g );
116+
if ( !vlayer->changeGeometry( mPressedFid, g ) )
117+
{
118+
vlayer->destroyEditCommand();
119+
return;
120+
}
117121
vlayer->endEditCommand();
118122
vlayer->triggerRepaint();
119123
}

src/app/qgsmaptoolsimplify.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,30 @@ void QgsMapToolSimplify::storeSimplified()
176176
double layerTolerance = QgsTolerance::toleranceInMapUnits( mTolerance, vlayer, mCanvas->mapSettings(), mToleranceUnits );
177177

178178
vlayer->beginEditCommand( tr( "Geometry simplified" ) );
179+
bool success = true;
179180
Q_FOREACH ( const QgsFeature& feat, mSelectedFeatures )
180181
{
181182
if ( QgsGeometry* g = feat.constGeometry()->simplify( layerTolerance ) )
182183
{
183-
vlayer->changeGeometry( feat.id(), g );
184+
if ( !vlayer->changeGeometry( feat.id(), g ) )
185+
{
186+
success = false;
187+
}
184188
delete g;
189+
190+
if ( !success )
191+
break;
185192
}
186193
}
187-
vlayer->endEditCommand();
188-
189-
clearSelection();
194+
if ( success )
195+
{
196+
vlayer->endEditCommand();
197+
clearSelection();
198+
}
199+
else
200+
{
201+
vlayer->destroyEditCommand();
202+
}
190203

191204
vlayer->triggerRepaint();
192205
}

src/core/qgsofflineediting.cpp

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ extern "C"
5454
#define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin"
5555
#define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath"
5656

57-
QgsOfflineEditing::QgsOfflineEditing()
57+
QgsOfflineEditing::QgsOfflineEditing():
58+
syncError( false )
5859
{
5960
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( layerAdded( QgsMapLayer* ) ) );
6061
}
@@ -261,6 +262,7 @@ void QgsOfflineEditing::synchronize()
261262
updateVisibilityPresets( offlineLayer, remoteLayer );
262263

263264
// apply layer edit log
265+
syncError = false;
264266
QString qgisLayerId = layer->id();
265267
QString sql = QString( "SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
266268
int layerId = sqlQueryInt( db, sql, -1 );
@@ -276,37 +278,49 @@ void QgsOfflineEditing::synchronize()
276278
QgsDebugMsgLevel( "Apply commits chronologically", 4 );
277279
// apply commits chronologically
278280
applyAttributesAdded( remoteLayer, db, layerId, i );
279-
applyAttributeValueChanges( offlineLayer, remoteLayer, db, layerId, i );
280-
applyGeometryChanges( remoteLayer, db, layerId, i );
281+
if ( !syncError )
282+
applyAttributeValueChanges( offlineLayer, remoteLayer, db, layerId, i );
283+
if ( !syncError )
284+
applyGeometryChanges( remoteLayer, db, layerId, i );
281285
}
282286

283-
applyFeaturesAdded( offlineLayer, remoteLayer, db, layerId );
284-
applyFeaturesRemoved( remoteLayer, db, layerId );
287+
if ( !syncError )
288+
applyFeaturesAdded( offlineLayer, remoteLayer, db, layerId );
289+
if ( !syncError )
290+
applyFeaturesRemoved( remoteLayer, db, layerId );
285291

286-
if ( remoteLayer->commitChanges() )
292+
if ( !syncError )
287293
{
288-
// update fid lookup
289-
updateFidLookup( remoteLayer, db, layerId );
290-
291-
// clear edit log for this layer
292-
sql = QString( "DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
293-
sqlExec( db, sql );
294-
sql = QString( "DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
295-
sqlExec( db, sql );
296-
sql = QString( "DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
297-
sqlExec( db, sql );
298-
sql = QString( "DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
299-
sqlExec( db, sql );
300-
sql = QString( "DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
301-
sqlExec( db, sql );
302-
303-
// reset commitNo
304-
QString sql = QString( "UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
305-
sqlExec( db, sql );
294+
if ( remoteLayer->commitChanges() )
295+
{
296+
// update fid lookup
297+
updateFidLookup( remoteLayer, db, layerId );
298+
299+
// clear edit log for this layer
300+
sql = QString( "DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
301+
sqlExec( db, sql );
302+
sql = QString( "DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
303+
sqlExec( db, sql );
304+
sql = QString( "DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
305+
sqlExec( db, sql );
306+
sql = QString( "DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
307+
sqlExec( db, sql );
308+
sql = QString( "DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
309+
sqlExec( db, sql );
310+
311+
// reset commitNo
312+
QString sql = QString( "UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
313+
sqlExec( db, sql );
314+
}
315+
else
316+
{
317+
showWarning( remoteLayer->commitErrors().join( "\n" ) );
318+
}
306319
}
307320
else
308321
{
309-
showWarning( remoteLayer->commitErrors().join( "\n" ) );
322+
remoteLayer->rollBack();
323+
showWarning( tr( "Syncronization failed" ) );
310324
}
311325
}
312326
else
@@ -688,7 +702,8 @@ QgsVectorLayer* QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlit
688702
f.geometry()->setGeometry( geom );
689703
}
690704

691-
newLayer->addFeature( f, false );
705+
if ( !newLayer->addFeature( f, false ) )
706+
return nullptr;
692707

693708
emit progressUpdated( featureCount++ );
694709
}
@@ -762,7 +777,11 @@ void QgsOfflineEditing::applyAttributesAdded( QgsVectorLayer* remoteLayer, sqlit
762777
{
763778
QString typeName = typeNameLookup[ field.type()];
764779
field.setTypeName( typeName );
765-
remoteLayer->addAttribute( field );
780+
if ( !remoteLayer->addAttribute( field ) )
781+
{
782+
syncError = true;
783+
return;
784+
}
766785
}
767786
else
768787
{
@@ -830,7 +849,11 @@ void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer* offlineLayer, QgsVec
830849

831850
f.setAttributes( newAttrs );
832851

833-
remoteLayer->addFeature( f, false );
852+
if ( !remoteLayer->addFeature( f, false ) )
853+
{
854+
syncError = true;
855+
return;
856+
}
834857

835858
emit progressUpdated( i++ );
836859
}
@@ -847,7 +870,11 @@ void QgsOfflineEditing::applyFeaturesRemoved( QgsVectorLayer* remoteLayer, sqlit
847870
for ( QgsFeatureIds::const_iterator it = values.begin(); it != values.end(); ++it )
848871
{
849872
QgsFeatureId fid = remoteFid( db, layerId, *it );
850-
remoteLayer->deleteFeature( fid );
873+
if ( !remoteLayer->deleteFeature( fid ) )
874+
{
875+
syncError = true;
876+
return;
877+
}
851878

852879
emit progressUpdated( i++ );
853880
}
@@ -866,7 +893,11 @@ void QgsOfflineEditing::applyAttributeValueChanges( QgsVectorLayer* offlineLayer
866893
{
867894
QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
868895
QgsDebugMsgLevel( QString( "Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );
869-
remoteLayer->changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
896+
if ( !remoteLayer->changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value ) )
897+
{
898+
syncError = true;
899+
return;
900+
}
870901

871902
emit progressUpdated( i + 1 );
872903
}
@@ -882,7 +913,11 @@ void QgsOfflineEditing::applyGeometryChanges( QgsVectorLayer* remoteLayer, sqlit
882913
for ( int i = 0; i < values.size(); i++ )
883914
{
884915
QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
885-
remoteLayer->changeGeometry( fid, QgsGeometry::fromWkt( values.at( i ).geom_wkt ) );
916+
if ( !remoteLayer->changeGeometry( fid, QgsGeometry::fromWkt( values.at( i ).geom_wkt ) ) )
917+
{
918+
syncError = true;
919+
return;
920+
}
886921

887922
emit progressUpdated( i + 1 );
888923
}

src/core/qgsofflineediting.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ class CORE_EXPORT QgsOfflineEditing : public QObject
153153
};
154154
typedef QList<GeometryChange> GeometryChanges;
155155
GeometryChanges sqlQueryGeometryChanges( sqlite3* db, const QString& sql );
156+
/**
157+
* in case of sync error the flag is set to true
158+
*/
159+
bool syncError;
160+
156161

157162
private slots:
158163
void layerAdded( QgsMapLayer* layer );

src/core/qgsvectordataprovider.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,10 +714,37 @@ QgsGeometry* QgsVectorDataProvider::convertToProviderType( const QgsGeometry* ge
714714
outputGeom->addMValue();
715715
}
716716

717+
// remove Z if provider does not have
718+
// control added to fix https://issues.qgis.org/issues/16927
719+
if ( !QgsWKBTypes::hasZ( providerGeomType ) && QgsWKBTypes::hasZ( geometry->wkbType() ) )
720+
{
721+
if ( !outputGeom )
722+
{
723+
outputGeom = geometry->clone();
724+
}
725+
outputGeom->dropZValue();
726+
}
727+
728+
// remove M if provider does not have
729+
// control added as follow-up of https://issues.qgis.org/issues/16927
730+
if ( !QgsWKBTypes::hasM( providerGeomType ) && QgsWKBTypes::hasM( geometry->wkbType() ) )
731+
{
732+
if ( !outputGeom )
733+
{
734+
outputGeom = geometry->clone();
735+
}
736+
outputGeom->dropMValue();
737+
}
738+
717739
if ( outputGeom )
718740
{
719741
return new QgsGeometry( outputGeom );
720742
}
743+
744+
QString msg = tr( "Geometry type %1 not compatible with provider type %2.", "not compatible geometry" )
745+
.arg( QgsWKBTypes::displayString( geometry->wkbType() ) )
746+
.arg( QgsWKBTypes::displayString( providerGeomType ) );
747+
const_cast<QgsVectorDataProvider*>( this )->pushError( msg );
721748
return nullptr;
722749
}
723750

0 commit comments

Comments
 (0)