# Small geometry processing

* changes SimpleGeomForm behaviour on small geometries (<1m²): These geometries will now be dismissed on processing
* adds a new info message in case of automatically removed geometries on saving
* updates tests
This commit is contained in:
2025-10-15 09:50:59 +02:00
parent cd2949fc03
commit 442f3ceb37
15 changed files with 148 additions and 68 deletions

View File

@@ -25,8 +25,8 @@ class SimpleGeomForm(BaseForm):
""" A geometry form for rendering geometry read-only using a widget
"""
read_only = True
geometry_simplified = False
read_only: bool = True
_geometry_simplified: bool = False
output = JSONField(
label=_("Geometry"),
help_text=_(""),
@@ -34,6 +34,7 @@ class SimpleGeomForm(BaseForm):
required=False,
disabled=False,
)
_num_geometries_ignored: int = 0
def __init__(self, *args, **kwargs):
self.read_only = kwargs.pop("read_only", True)
@@ -48,6 +49,7 @@ class SimpleGeomForm(BaseForm):
raise AttributeError
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
self._set_geojson_properties(geojson, title=self.instance.identifier or None)
geom = json.dumps(geojson)
except AttributeError:
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
@@ -61,7 +63,7 @@ class SimpleGeomForm(BaseForm):
is_valid = True
# Get geojson from form
geom = self.data["output"]
geom = self.data.get("output", None)
if geom is None or len(geom) == 0:
# empty geometry is a valid geometry
self.cleaned_data["output"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
@@ -100,7 +102,13 @@ class SimpleGeomForm(BaseForm):
is_valid &= False
return is_valid
is_valid &= self.__is_area_valid(g)
is_area_valid = self.__is_area_valid(g)
if not is_area_valid:
# Geometries with an invalid size will not be saved to the db
# We assume these are malicious snippets which are not supposed to be in the geometry in the first place
self._num_geometries_ignored += 1
continue
g = Polygon.from_ewkt(g.ewkt)
is_valid &= g.valid
if not g.valid:
@@ -147,15 +155,6 @@ class SimpleGeomForm(BaseForm):
"""
is_area_valid = geom.area > 1 # > 1m² (SRID:25832)
if not is_area_valid:
self.add_error(
"output",
_("Geometry must be greater than 1m². Currently is {}").format(
float(geom.area)
)
)
return is_area_valid
def __simplify_geometry(self, geom, max_vert: int):
@@ -208,13 +207,29 @@ class SimpleGeomForm(BaseForm):
if not is_vertices_num_valid:
geometry.geom = self.__simplify_geometry(geometry.geom, max_vert=GEOM_MAX_VERTICES)
geometry.save()
self.geometry_simplified = True
self._geometry_simplified = True
# Start parcel update and geometry conflict checking procedure in a background process
celery_update_parcels.delay(geometry.id)
celery_check_for_geometry_conflicts.delay(geometry.id)
return geometry
def get_num_geometries_ignored(self):
""" Returns the number of geometries which had to be ignored for various reasons
Returns:
"""
return self._num_geometries_ignored
def has_geometry_simplified(self):
""" Returns whether the geometry has been simplified or not.
Returns:
"""
return self._geometry_simplified
def __flatten_geom_to_2D(self, geom):
"""
Enforces a given OGRGeometry from higher dimensions into 2D
@@ -225,11 +240,12 @@ class SimpleGeomForm(BaseForm):
geom = gdal.OGRGeometry(g_wkt)
return geom
def _set_properties(self, geojson: dict, title: str):
def _set_geojson_properties(self, geojson: dict, title: str = None):
""" Toggles the editable property of the geojson for proper handling in map client
Args:
geojson (dict): The GeoJson
title (str): An alternative title for the geometry
Returns:
geojson (dict): The altered GeoJson