Skip to content

Commit 53d90b5

Browse files
committedJun 12, 2017
fix Postgis Merge selected features regression: port 2.18 fixes #15741
1 parent 9626d2f commit 53d90b5

File tree

4 files changed

+379
-0
lines changed

4 files changed

+379
-0
lines changed
 

‎src/core/qgsvectordataprovider.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
5252
Q_OBJECT
5353

5454
friend class QgsTransaction;
55+
friend class QgsVectorLayerEditBuffer;
5556

5657
public:
5758

‎src/core/qgsvectorlayereditbuffer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,35 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors )
302302
// no yes => changeAttributeValues
303303
// yes yes => changeFeatures
304304

305+
// to fix https://issues.qgis.org/issues/15741
306+
// first of all check if feature to add is compatible with provider type
307+
// this check have to be done before all checks to avoid to clear internal
308+
// buffer if some of next steps success.
309+
if ( success && !mAddedFeatures.isEmpty() )
310+
{
311+
if ( cap & QgsVectorDataProvider::AddFeatures )
312+
{
313+
for ( QgsFeature f : mAddedFeatures )
314+
{
315+
if (( ! f.geometry() ) || ( f.geometry()->isEmpty() ) ||
316+
( f.geometry()->wkbType() == provider->geometryType() ) )
317+
continue;
318+
319+
if ( ! provider->convertToProviderType( f.geometry() ) )
320+
{
321+
commitErrors << tr( "ERROR: %n feature(s) not added - geometry type is not compatible with the current layer.", "not added features count", mAddedFeatures.size() );
322+
success = false;
323+
break;
324+
}
325+
}
326+
}
327+
else
328+
{
329+
commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
330+
success = false;
331+
}
332+
}
333+
305334
//
306335
// update geometries
307336
//

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ ADD_PYTHON_TEST(PyQgsUnitTypes test_qgsunittypes.py)
106106
ADD_PYTHON_TEST(PyQgsVectorColorRamp test_qgsvectorcolorramp.py)
107107
ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py)
108108
ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
109+
ADD_PYTHON_TEST(PyQgsVectorLayerEditBuffer test_qgsvectorlayereditbuffer.py)
109110
ADD_PYTHON_TEST(PyQgsZonalStatistics test_qgszonalstatistics.py)
110111
ADD_PYTHON_TEST(PyQgsMapLayerRegistry test_qgsmaplayerregistry.py)
111112
ADD_PYTHON_TEST(PyQgsVirtualLayerProvider test_provider_virtual.py)
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsVectorLayerEditBuffer.
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '15/07/2016'
11+
__copyright__ = 'Copyright 2016, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.PyQt.QtCore import QVariant
18+
19+
from qgis.core import (QgsVectorLayer,
20+
QgsFeature,
21+
QgsGeometry,
22+
QgsPoint,
23+
QgsField)
24+
from qgis.testing import start_app, unittest
25+
start_app()
26+
27+
28+
def createEmptyLayer():
29+
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
30+
"addfeat", "memory")
31+
assert layer.isValid()
32+
return layer
33+
34+
35+
def createLayerWithOnePoint():
36+
layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
37+
"addfeat", "memory")
38+
pr = layer.dataProvider()
39+
f = QgsFeature()
40+
f.setAttributes(["test", 123])
41+
f.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
42+
assert pr.addFeatures([f])
43+
assert layer.pendingFeatureCount() == 1
44+
return layer
45+
46+
47+
def createEmptyLinestringLayer():
48+
layer = QgsVectorLayer("Linestring?field=fldtxt:string&field=fldint:integer",
49+
"addfeat", "memory")
50+
assert layer.isValid()
51+
return layer
52+
53+
54+
class TestQgsVectorLayerEditBuffer(unittest.TestCase):
55+
56+
def testAddFeatures(self):
57+
# test adding features to an edit buffer
58+
layer = createEmptyLayer()
59+
self.assertTrue(layer.startEditing())
60+
61+
self.assertEqual(layer.editBuffer().addedFeatures(), {})
62+
self.assertFalse(1 in layer.editBuffer().addedFeatures())
63+
self.assertFalse(3 in layer.editBuffer().addedFeatures())
64+
65+
# add two features
66+
f1 = QgsFeature(layer.fields(), 1)
67+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
68+
f1.setAttributes(["test", 123])
69+
self.assertTrue(layer.addFeature(f1))
70+
71+
f2 = QgsFeature(layer.fields(), 2)
72+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
73+
f2.setAttributes(["test2", 246])
74+
75+
self.assertTrue(layer.addFeature(f2))
76+
77+
# test contents of buffer
78+
added = layer.editBuffer().addedFeatures()
79+
new_feature_ids = list(added.keys())
80+
self.assertEqual(added[new_feature_ids[0]]['fldtxt'], 'test2')
81+
self.assertEqual(added[new_feature_ids[0]]['fldint'], 246)
82+
self.assertEqual(added[new_feature_ids[1]]['fldtxt'], 'test')
83+
self.assertEqual(added[new_feature_ids[1]]['fldint'], 123)
84+
85+
self.assertTrue(new_feature_ids[0] in layer.editBuffer().addedFeatures())
86+
self.assertTrue(new_feature_ids[1] in layer.editBuffer().addedFeatures())
87+
88+
# check if error in case adding not adaptable geometry
89+
# eg. a Multiline in a Line
90+
layer = createEmptyLinestringLayer()
91+
self.assertTrue(layer.startEditing())
92+
93+
self.assertEqual(layer.editBuffer().addedFeatures(), {})
94+
self.assertFalse(1 in layer.editBuffer().addedFeatures())
95+
self.assertFalse(3 in layer.editBuffer().addedFeatures())
96+
97+
# add a features with a multi line geometry of not touched lines =>
98+
# cannot be forced to be linestring
99+
multiline = [
100+
[QgsPoint(1, 1), QgsPoint(2, 2)],
101+
[QgsPoint(3, 3), QgsPoint(4, 4)],
102+
]
103+
f1 = QgsFeature(layer.fields(), 1)
104+
f1.setGeometry(QgsGeometry.fromMultiPolyline(multiline))
105+
f1.setAttributes(["test", 123])
106+
self.assertTrue(layer.addFeatures([f1]))
107+
self.assertFalse(layer.commitChanges())
108+
109+
def testAddMultipleFeatures(self):
110+
# test adding multiple features to an edit buffer
111+
layer = createEmptyLayer()
112+
self.assertTrue(layer.startEditing())
113+
114+
self.assertEqual(layer.editBuffer().addedFeatures(), {})
115+
self.assertFalse(1 in layer.editBuffer().addedFeatures())
116+
self.assertFalse(3 in layer.editBuffer().addedFeatures())
117+
118+
# add two features
119+
f1 = QgsFeature(layer.fields(), 1)
120+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
121+
f1.setAttributes(["test", 123])
122+
f2 = QgsFeature(layer.fields(), 2)
123+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
124+
f2.setAttributes(["test2", 246])
125+
126+
self.assertTrue(layer.addFeatures([f1, f2]))
127+
128+
# test contents of buffer
129+
added = layer.editBuffer().addedFeatures()
130+
new_feature_ids = list(added.keys())
131+
self.assertEqual(added[new_feature_ids[0]]['fldtxt'], 'test2')
132+
self.assertEqual(added[new_feature_ids[0]]['fldint'], 246)
133+
self.assertEqual(added[new_feature_ids[1]]['fldtxt'], 'test')
134+
self.assertEqual(added[new_feature_ids[1]]['fldint'], 123)
135+
136+
self.assertTrue(new_feature_ids[0] in layer.editBuffer().addedFeatures())
137+
self.assertTrue(new_feature_ids[1] in layer.editBuffer().addedFeatures())
138+
139+
def testDeleteFeatures(self):
140+
# test deleting features from an edit buffer
141+
142+
# make a layer with two features
143+
layer = createEmptyLayer()
144+
self.assertTrue(layer.startEditing())
145+
146+
# add two features
147+
f1 = QgsFeature(layer.fields(), 1)
148+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
149+
f1.setAttributes(["test", 123])
150+
self.assertTrue(layer.addFeature(f1))
151+
152+
f2 = QgsFeature(layer.fields(), 2)
153+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
154+
f2.setAttributes(["test2", 246])
155+
self.assertTrue(layer.addFeature(f2))
156+
157+
layer.commitChanges()
158+
layer.startEditing()
159+
160+
self.assertEqual(layer.editBuffer().deletedFeatureIds(), [])
161+
self.assertFalse(1 in layer.editBuffer().deletedFeatureIds())
162+
self.assertFalse(2 in layer.editBuffer().deletedFeatureIds())
163+
164+
# delete a feature
165+
layer.deleteFeature(1)
166+
167+
# test contents of buffer
168+
self.assertEqual(layer.editBuffer().deletedFeatureIds(), [1])
169+
self.assertTrue(1 in layer.editBuffer().deletedFeatureIds())
170+
self.assertFalse(2 in layer.editBuffer().deletedFeatureIds())
171+
172+
# delete a feature
173+
layer.deleteFeature(2)
174+
175+
# test contents of buffer
176+
self.assertEqual(set(layer.editBuffer().deletedFeatureIds()), set([1, 2]))
177+
self.assertTrue(1 in layer.editBuffer().deletedFeatureIds())
178+
self.assertTrue(2 in layer.editBuffer().deletedFeatureIds())
179+
180+
def testDeleteMultipleFeatures(self):
181+
# test deleting multiple features from an edit buffer
182+
183+
# make a layer with two features
184+
layer = createEmptyLayer()
185+
self.assertTrue(layer.startEditing())
186+
187+
# add two features
188+
f1 = QgsFeature(layer.fields(), 1)
189+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
190+
f1.setAttributes(["test", 123])
191+
self.assertTrue(layer.addFeature(f1))
192+
193+
f2 = QgsFeature(layer.fields(), 2)
194+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
195+
f2.setAttributes(["test2", 246])
196+
self.assertTrue(layer.addFeature(f2))
197+
198+
layer.commitChanges()
199+
layer.startEditing()
200+
201+
self.assertEqual(layer.editBuffer().deletedFeatureIds(), [])
202+
self.assertFalse(1 in layer.editBuffer().addedFeatures())
203+
self.assertFalse(2 in layer.editBuffer().addedFeatures())
204+
205+
# delete features
206+
layer.deleteFeatures([1, 2])
207+
208+
# test contents of buffer
209+
self.assertEqual(set(layer.editBuffer().deletedFeatureIds()), set([1, 2]))
210+
self.assertTrue(1 in layer.editBuffer().deletedFeatureIds())
211+
self.assertTrue(2 in layer.editBuffer().deletedFeatureIds())
212+
213+
def testChangeAttributeValues(self):
214+
# test changing attributes values from an edit buffer
215+
216+
# make a layer with two features
217+
layer = createEmptyLayer()
218+
self.assertTrue(layer.startEditing())
219+
220+
# add two features
221+
f1 = QgsFeature(layer.fields(), 1)
222+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
223+
f1.setAttributes(["test", 123])
224+
self.assertTrue(layer.addFeature(f1))
225+
226+
f2 = QgsFeature(layer.fields(), 2)
227+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
228+
f2.setAttributes(["test2", 246])
229+
self.assertTrue(layer.addFeature(f2))
230+
231+
layer.commitChanges()
232+
layer.startEditing()
233+
234+
self.assertEqual(layer.editBuffer().changedAttributeValues(), {})
235+
self.assertFalse(1 in layer.editBuffer().changedAttributeValues())
236+
self.assertFalse(2 in layer.editBuffer().changedAttributeValues())
237+
238+
# change attribute values
239+
layer.changeAttributeValue(1, 0, 'a')
240+
241+
# test contents of buffer
242+
self.assertEqual(list(layer.editBuffer().changedAttributeValues().keys()), [1])
243+
self.assertEqual(layer.editBuffer().changedAttributeValues()[1], {0: 'a'})
244+
self.assertTrue(1 in layer.editBuffer().changedAttributeValues())
245+
self.assertFalse(2 in layer.editBuffer().changedAttributeValues())
246+
247+
layer.changeAttributeValue(2, 1, 5)
248+
249+
# test contents of buffer
250+
self.assertEqual(set(layer.editBuffer().changedAttributeValues().keys()), set([1, 2]))
251+
self.assertEqual(layer.editBuffer().changedAttributeValues()[1], {0: 'a'})
252+
self.assertEqual(layer.editBuffer().changedAttributeValues()[2], {1: 5})
253+
self.assertTrue(1 in layer.editBuffer().changedAttributeValues())
254+
self.assertTrue(2 in layer.editBuffer().changedAttributeValues())
255+
256+
def testChangeGeometry(self):
257+
# test changing geometries values from an edit buffer
258+
259+
# make a layer with two features
260+
layer = createEmptyLayer()
261+
self.assertTrue(layer.startEditing())
262+
263+
# add two features
264+
f1 = QgsFeature(layer.fields(), 1)
265+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 2)))
266+
f1.setAttributes(["test", 123])
267+
self.assertTrue(layer.addFeature(f1))
268+
269+
f2 = QgsFeature(layer.fields(), 2)
270+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 4)))
271+
f2.setAttributes(["test2", 246])
272+
self.assertTrue(layer.addFeature(f2))
273+
274+
layer.commitChanges()
275+
layer.startEditing()
276+
277+
self.assertEqual(layer.editBuffer().changedGeometries(), {})
278+
self.assertFalse(1 in layer.editBuffer().changedGeometries())
279+
self.assertFalse(2 in layer.editBuffer().changedGeometries())
280+
281+
# change attribute values
282+
layer.changeGeometry(1, QgsGeometry.fromPoint(QgsPoint(10, 20)))
283+
284+
# test contents of buffer
285+
self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1])
286+
self.assertEqual(layer.editBuffer().changedGeometries()[1].geometry().x(), 10)
287+
self.assertTrue(1 in layer.editBuffer().changedGeometries())
288+
self.assertFalse(2 in layer.editBuffer().changedGeometries())
289+
290+
layer.changeGeometry(2, QgsGeometry.fromPoint(QgsPoint(20, 40)))
291+
292+
# test contents of buffer
293+
self.assertEqual(set(layer.editBuffer().changedGeometries().keys()), set([1, 2]))
294+
self.assertEqual(layer.editBuffer().changedGeometries()[1].geometry().x(), 10)
295+
self.assertEqual(layer.editBuffer().changedGeometries()[2].geometry().x(), 20)
296+
self.assertTrue(1 in layer.editBuffer().changedGeometries())
297+
self.assertTrue(2 in layer.editBuffer().changedGeometries())
298+
299+
def testDeleteAttribute(self):
300+
# test deleting attributes from an edit buffer
301+
302+
layer = createEmptyLayer()
303+
layer.startEditing()
304+
305+
self.assertEqual(layer.editBuffer().deletedAttributeIds(), [])
306+
self.assertFalse(0 in layer.editBuffer().deletedAttributeIds())
307+
self.assertFalse(1 in layer.editBuffer().deletedAttributeIds())
308+
309+
# delete attribute
310+
layer.deleteAttribute(0)
311+
312+
# test contents of buffer
313+
self.assertEqual(layer.editBuffer().deletedAttributeIds(), [0])
314+
self.assertTrue(0 in layer.editBuffer().deletedAttributeIds())
315+
self.assertFalse(1 in layer.editBuffer().deletedAttributeIds())
316+
317+
# delete remaining attribute (now at position 0)
318+
layer.deleteAttribute(0)
319+
320+
# test contents of buffer
321+
self.assertEqual(layer.editBuffer().deletedAttributeIds(), [0, 1])
322+
self.assertTrue(0 in layer.editBuffer().deletedAttributeIds())
323+
self.assertTrue(1 in layer.editBuffer().deletedAttributeIds())
324+
325+
def testAddAttribute(self):
326+
# test adding attributes to an edit buffer
327+
328+
layer = createEmptyLayer()
329+
layer.startEditing()
330+
331+
self.assertEqual(layer.editBuffer().addedAttributes(), [])
332+
333+
# add attribute
334+
layer.addAttribute(QgsField('new1', QVariant.String))
335+
336+
# test contents of buffer
337+
self.assertEqual(layer.editBuffer().addedAttributes()[0].name(), 'new1')
338+
339+
# add another attribute
340+
layer.addAttribute(QgsField('new2', QVariant.String))
341+
342+
# test contents of buffer
343+
self.assertEqual(layer.editBuffer().addedAttributes()[0].name(), 'new1')
344+
self.assertEqual(layer.editBuffer().addedAttributes()[1].name(), 'new2')
345+
346+
347+
if __name__ == '__main__':
348+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.