More tests for Z/M add back

This commit is contained in:
Alessandro Pasotti 2018-09-11 10:47:38 +02:00 committed by Nyall Dawson
parent 4549ee5fff
commit 08f79bed43
2 changed files with 107 additions and 30 deletions

View File

@ -69,14 +69,13 @@ def execute(alg, parameters, context=None, feedback=None):
return False, {}
def make_feature_compatible(new_features, input_layer):
"""Try to make new features compatible with old feature by:
def make_features_compatible(new_features, input_layer, old_feature=None):
"""Try to make the new features compatible with old features by:
- converting single to multi part
- dropping additional attributes
- adding back M/Z values
:param new_features: new features
:type new_features: list of QgsFeatures
:param input_layer: input layer
@ -95,27 +94,24 @@ def make_feature_compatible(new_features, input_layer):
new_geom.convertToMultiType()
new_f.setGeometry(new_geom)
# Drop Z/M
if ((QgsWkbTypes.hasZ(new_f.geometry().wkbType()) and not QgsWkbTypes.hasZ(input_layer.wkbType())) or
(QgsWkbTypes.hasM(new_f.geometry().wkbType()) and not QgsWkbTypes.hasM(input_layer.wkbType()))):
# FIXME: there must be a better way!!!
if new_f.geometry().type() == QgsWkbTypes.PointGeometry:
if new_f.geometry().isMultipart():
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPoint().asWkt())
else:
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPoint().asWkt())
elif new_f.geometry().type() == QgsWkbTypes.PolygonGeometry:
if new_f.geometry().isMultipart():
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPolygon().asWkt())
else:
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPolygon().asWkt())
elif new_f.geometry().type() == QgsWkbTypes.LineGeometry: # Linestring
if new_f.geometry().isMultipart():
new_geom = QgsGeometry.fromWkt(new_f.geometry().asPolyline().asWkt())
else:
new_geom = QgsGeometry.fromWkt(new_f.geometry().asMultiPolyline().asWkt())
else:
new_geom = QgsGeometry()
if (new_f.geometry().constGet().is3D() and not QgsWkbTypes.hasZ(input_layer.wkbType())):
new_geom = new_f.geometry()
new_geom.get().dropZValue()
new_f.setGeometry(new_geom)
if (new_f.geometry().constGet().isMeasure() and not QgsWkbTypes.hasM(input_layer.wkbType())):
new_geom = new_f.geometry()
new_geom.get().dropMValue()
new_f.setGeometry(new_geom)
# Add Z/M back (set it to 0)
if (old_feature is not None and not new_f.geometry().constGet().is3D() and QgsWkbTypes.hasZ(input_layer.wkbType())):
new_geom = new_f.geometry()
new_geom.get().addZValue(0.0)
new_f.setGeometry(new_geom)
if (old_feature is not None and not new_f.geometry().constGet().isMeasure() and QgsWkbTypes.hasM(input_layer.wkbType())):
new_geom = new_f.geometry()
new_geom.get().addMValue(0.0)
new_f.setGeometry(new_geom)
if len(new_f.attributes()) > len(input_layer.fields()):
f = QgsFeature(input_layer.fields())
f.setGeometry(new_f.geometry())
@ -180,7 +176,7 @@ def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=N
feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures()
for f in feature_iterator:
new_features = alg.processFeature(f, context, feedback)
new_features = make_feature_compatible(new_features, active_layer)
new_features = make_features_compatible(new_features, active_layer, f)
if len(new_features) == 0:
active_layer.deleteFeature(f.id())
elif len(new_features) == 1:
@ -209,7 +205,7 @@ def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=N
active_layer.deleteFeatures(active_layer.selectedFeatureIds())
new_features = []
for f in result_layer.getFeatures():
new_features.append(make_feature_compatible([f], active_layer))
new_features.append(make_features_compatible([f], active_layer))
# Get the new ids
old_ids = set([f.id() for f in active_layer.getFeatures(req)])

View File

@ -17,7 +17,7 @@ from qgis.core import (
QgsFeature, QgsGeometry, QgsSettings, QgsApplication, QgsMemoryProviderUtils, QgsWkbTypes, QgsField, QgsFields, QgsProcessingFeatureSourceDefinition, QgsProcessingContext, QgsProcessingFeedback, QgsCoordinateReferenceSystem, QgsProject, QgsProcessingException
)
from processing.core.Processing import Processing
from processing.gui.AlgorithmExecutor import execute_in_place_run
from processing.gui.AlgorithmExecutor import execute_in_place_run, make_features_compatible
from qgis.testing import start_app, unittest
from qgis.PyQt.QtTest import QSignalSpy
from qgis.analysis import QgsNativeAlgorithms
@ -45,10 +45,10 @@ class TestQgsProcessingInPlace(unittest.TestCase):
f1 = QgsFeature(cls.vl.fields())
f1['int_f'] = 1
f1.setGeometry(QgsGeometry.fromWkt('point(9 45)'))
f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
f2 = QgsFeature(cls.vl.fields())
f2['int_f'] = 2
f2.setGeometry(QgsGeometry.fromWkt('point(9.5 45.6)'))
f2.setGeometry(QgsGeometry.fromWkt('Point(9.5 45.6)'))
cls.vl.dataProvider().addFeatures([f1, f2])
assert cls.vl.isValid()
@ -199,9 +199,90 @@ class TestQgsProcessingInPlace(unittest.TestCase):
# Check selected
self.assertEqual(len(self.vl.selectedFeatureIds()), 3)
def test_make_compatible(self):
def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1], old_feature=None):
fields = QgsFields()
wkb_type = getattr(QgsWkbTypes, layer_wkb_name)
fields.append(QgsField('int_f', QVariant.Int))
layer = QgsMemoryProviderUtils.createMemoryLayer(
'%s_layer' % layer_wkb_name, fields, wkb_type, QgsCoordinateReferenceSystem(4326))
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), wkb_type)
layer.startEditing()
f = QgsFeature(layer.fields())
f.setAttributes(attrs)
f.setGeometry(QgsGeometry.fromWkt(feature_wkt))
self.assertTrue(f.isValid())
# Fix it!
new_features = make_features_compatible([f], layer, old_feature)
self.assertEqual(f.geometry().wkbType(), wkb_type)
self.assertTrue(layer.addFeatures(new_features), "Fail: %s - %s - %s" % (feature_wkt, attrs, layer_wkb_name))
return layer, new_features[0]
def test_make_features_compatible(self):
"""Test fixer function"""
pass
# Test failure
with self.assertRaises(AssertionError):
self._make_compatible_tester('LineString (1 1, 2 2, 3 3)', 'Point')
self._make_compatible_tester('Point(1 1)', 'Point')
self._make_compatible_tester('Point(1 1)', 'Point', [1, 'nope'])
self._make_compatible_tester('Point z (1 1 3)', 'Point')
self._make_compatible_tester('Point z (1 1 3)', 'PointZ')
# Adding Z back
old_feature = QgsFeature(self.vl.fields())
old_feature.setAttributes([1])
old_feature.setGeometry(QgsGeometry.fromWkt('Point z (1 1 3)'))
l, f = self._make_compatible_tester('Point (1 1)', 'PointZ', old_feature=old_feature)
self.assertEqual(f.geometry().get().z(), 0)
# Adding M back
old_feature = QgsFeature(self.vl.fields())
old_feature.setAttributes([1])
old_feature.setGeometry(QgsGeometry.fromWkt('Point m (1 1 3)'))
l, f = self._make_compatible_tester('Point (1 1)', 'PointM', old_feature=old_feature)
self.assertEqual(f.geometry().get().m(), 0)
self._make_compatible_tester('Point m (1 1 3)', 'Point')
self._make_compatible_tester('Point(1 3)', 'MultiPoint')
self._make_compatible_tester('MultiPoint((1 3), (2 2))', 'MultiPoint')
self._make_compatible_tester('Polygon((1 1, 2 2, 3 3, 1 1))', 'Polygon')
self._make_compatible_tester('Polygon((1 1, 2 2, 3 3, 1 1)', 'Polygon', [1, 'nope'])
self._make_compatible_tester('Polygon z ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'Polygon')
self._make_compatible_tester('Polygon z ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'PolygonZ')
# Adding Z back
old_feature = QgsFeature(self.vl.fields())
old_feature.setAttributes([1])
old_feature.setGeometry(QgsGeometry.fromWkt('Polygon z ((1 1 1, 2 2 2, 3 3 3 , 1 1 1))'))
l, f = self._make_compatible_tester('Polygon ((1 1, 2 2, 3 3, 1 1))', 'PolygonZ', old_feature=old_feature)
g = f.geometry().get()
print(g)
self.assertEqual(f.geometry().get().z(), 0)
# Adding M back
old_feature = QgsFeature(self.vl.fields())
old_feature.setAttributes([1])
old_feature.setGeometry(QgsGeometry.fromWkt('Polygon m ((1 1 1, 2 2 2, 3 3 3 , 1 1 1))'))
l, f = self._make_compatible_tester('Polygon ((1 1, 2 2, 3 3, 1 1))', 'PolygonM', old_feature=old_feature)
self.assertEqual(f.geometry().get().m(), 0)
self._make_compatible_tester('Polygon m ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'Polygon')
self._make_compatible_tester('Polygon m ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'PolygonM')
self._make_compatible_tester('Polygon((1 1, 2 2, 3 3, 1 1))', 'MultiPolygon')
self._make_compatible_tester('MultiPolygon(((1 1, 2 2, 3 3, 1 1)), ((1 1, 2 2, 3 3, 1 1)))', 'MultiPolygon')
self._make_compatible_tester('LineString((1 1, 2 2, 3 3, 1 1))', 'LineString')
self._make_compatible_tester('LineString((1 1, 2 2, 3 3, 1 1)', 'LineString', [1, 'nope'])
self._make_compatible_tester('LineString z ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'LineString')
self._make_compatible_tester('LineString z ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'LineStringZ')
self._make_compatible_tester('LineString m ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'LineString')
self._make_compatible_tester('LineString m ((1 1 1, 2 2 2, 3 3 3, 1 1 1))', 'LineStringM')
self._make_compatible_tester('LineString((1 1, 2 2, 3 3, 1 1))', 'MultiLineString')
self._make_compatible_tester('MultiLineString(((1 1, 2 2, 3 3, 1 1)), ((1 1, 2 2, 3 3, 1 1)))', 'MultiLineString')
if __name__ == '__main__':