From 63a824f9d9562746d81a447180ad5a702edcb046 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 12 Sep 2025 13:22:35 +0200 Subject: [PATCH] # Geometry form fix * fixes bugs in tests * refactors and simplifies geometry merging on GeometryForm --- .../tests/ecoaccount/test_workflow.py | 4 +-- ema/tests/test_workflow.py | 4 +-- intervention/tests/unit/test_forms.py | 2 +- konova/forms/geometry_form.py | 26 ++++++++++--------- konova/models/geometry.py | 2 +- konova/tests/test_views.py | 7 +++-- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py index 85f7db54..27eb0a99 100644 --- a/compensation/tests/ecoaccount/test_workflow.py +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -86,7 +86,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): new_title = self.create_dummy_string() new_identifier = self.create_dummy_string() new_comment = self.create_dummy_string() - new_geometry = MultiPolygon(srid=4326) # Create an empty geometry + new_geometry = self.create_dummy_geometry() test_conservation_office = self.get_conservation_office_code() test_deductable_surface = self.eco_account.deductable_surface + 100 @@ -103,7 +103,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): "identifier": new_identifier, "title": new_title, "comment": new_comment, - "geom": new_geometry.geojson, + "geom": self.create_geojson(new_geometry), "surface": test_deductable_surface, "conservation_office": test_conservation_office.id } diff --git a/ema/tests/test_workflow.py b/ema/tests/test_workflow.py index c6228112..7fb3c6a3 100644 --- a/ema/tests/test_workflow.py +++ b/ema/tests/test_workflow.py @@ -84,7 +84,7 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase): new_title = self.create_dummy_string() new_identifier = self.create_dummy_string() new_comment = self.create_dummy_string() - new_geometry = MultiPolygon(srid=4326) # Create an empty geometry + new_geometry = self.create_dummy_geometry() # Create an empty geometry test_conservation_office = self.get_conservation_office_code() check_on_elements = { @@ -99,7 +99,7 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase): "identifier": new_identifier, "title": new_title, "comment": new_comment, - "geom": new_geometry.geojson, + "geom": self.create_geojson(new_geometry), "conservation_office": test_conservation_office.id } self.client_user.post(url, post_data) diff --git a/intervention/tests/unit/test_forms.py b/intervention/tests/unit/test_forms.py index 1c4306c2..6435e21e 100644 --- a/intervention/tests/unit/test_forms.py +++ b/intervention/tests/unit/test_forms.py @@ -124,7 +124,7 @@ class EditInterventionFormTestCase(NewInterventionFormTestCase): self.assertIsNotNone(obj.responsible.handler) self.assertEqual(obj.title, data["title"]) self.assertEqual(obj.comment, data["comment"]) - self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001)) + self.assert_equal_geometries(test_geom, obj.geometry.geom) self.assertEqual(obj.legal.binding_date, today) self.assertEqual(obj.legal.registration_date, today) diff --git a/konova/forms/geometry_form.py b/konova/forms/geometry_form.py index e6ef3998..aadd977c 100644 --- a/konova/forms/geometry_form.py +++ b/konova/forms/geometry_form.py @@ -72,9 +72,8 @@ class SimpleGeomForm(BaseForm): # will be rendered again on failed submit self.initialize_form_field("geom", self.data["geom"]) - # Read geojson into gdal geometry - # HINT: This can be simplified if the geojson format holds data in epsg:4326 (GDAL provides direct creation for - # this case) + # Initialize features list with empty MultiPolygon, so that an empty input will result in a + # proper empty MultiPolygon object features = [] features_json = geom.get("features", []) accepted_ogr_types = [ @@ -102,19 +101,22 @@ class SimpleGeomForm(BaseForm): return is_valid is_valid &= self.__is_area_valid(g) - - polygon = Polygon.from_ewkt(g.ewkt) - is_valid &= polygon.valid - if not polygon.valid: - self.add_error("geom", polygon.valid_reason) + g = Polygon.from_ewkt(g.ewkt) + is_valid &= g.valid + if not g.valid: + self.add_error("geom", g.valid_reason) return is_valid - features.append(polygon) + if isinstance(g, Polygon): + features.append(g) + elif isinstance(g, MultiPolygon): + features.extend(list(g)) # Unionize all geometry features into one new MultiPolygon - form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP) - for feature in features: - form_geom = form_geom.union(feature) + if features: + form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union + else: + form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP) # Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided. form_geom = Geometry.cast_to_multipolygon(form_geom) diff --git a/konova/models/geometry.py b/konova/models/geometry.py index 1839f103..4f8b49d7 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -395,7 +395,7 @@ class Geometry(BaseResource): output_geom """ output_geom = input_geom - if input_geom.geom_type != "MultiPolygon": + if not isinstance(input_geom, MultiPolygon): output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP) return output_geom diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index 95fb8367..4977bfee 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -469,7 +469,7 @@ class BaseTestCase(TestCase): eco_account.save() return eco_account - def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon, tolerance = 0.001): + def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon, tolerance=0.001): """ Assert for geometries to be equal Transforms the geometries to matching srids before checking @@ -491,7 +491,10 @@ class BaseTestCase(TestCase): # transformation from one coordinate system into the other, which is valid geom1.transform(geom2.srid) geom2.transform(geom1.srid) - self.assertTrue(geom1.equals_exact(geom2, tolerance) or geom2.equals_exact(geom1, tolerance)) + self.assertTrue( + geom1.equals_exact(geom2, tolerance=tolerance), + msg=f"Difference is {abs(geom1.area - geom2.area)} with {geom1.area} and {geom2.area} in a tolerance of {tolerance}" + ) class BaseViewTestCase(BaseTestCase):