Compare commits
10 Commits
2c60d86177
...
1.13.7
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c4d64cc3d | |||
| fbde03caec | |||
| 43eb598d3f | |||
| b7fac0ae03 | |||
| 447ba942b5 | |||
| 6df47f1615 | |||
| e25d549a97 | |||
| 5e65b8f4dc | |||
| 22cddb9902 | |||
| c986bd0b92 |
@@ -53,7 +53,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% if deduction.intervention.recorded %}
|
{% if deduction.intervention.recorded %}
|
||||||
<em title="{% trans 'Recorded on' %} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
|
<em title="{% trans 'Recorded on' %} {{deduction.intervention.recorded.timestamp}} {% trans 'by' %} {{deduction.intervention.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
|
||||||
{% else %}
|
{% else %}
|
||||||
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
|
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class SimpleGeomForm(BaseForm):
|
|||||||
disabled=False,
|
disabled=False,
|
||||||
)
|
)
|
||||||
_num_geometries_ignored: int = 0
|
_num_geometries_ignored: int = 0
|
||||||
|
empty = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.read_only = kwargs.pop("read_only", True)
|
self.read_only = kwargs.pop("read_only", True)
|
||||||
@@ -49,11 +50,11 @@ class SimpleGeomForm(BaseForm):
|
|||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
||||||
self._set_geojson_properties(geojson, title=self.instance.identifier or None)
|
geojson = self._set_geojson_properties(geojson, title=self.instance.identifier or None)
|
||||||
geom = json.dumps(geojson)
|
geom = json.dumps(geojson)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
||||||
geom = ""
|
geom = json.dumps({})
|
||||||
self.empty = True
|
self.empty = True
|
||||||
|
|
||||||
self.initialize_form_field("output", geom)
|
self.initialize_form_field("output", geom)
|
||||||
@@ -62,17 +63,17 @@ class SimpleGeomForm(BaseForm):
|
|||||||
super().is_valid()
|
super().is_valid()
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
|
||||||
# Get geojson from form
|
# Make sure invalid geometry is properly rendered again to the user
|
||||||
geom = self.data.get("output", None)
|
# Therefore: write submitted data back into form field
|
||||||
if geom is None or len(geom) == 0:
|
# (does not matter whether we know if it is valid or invalid)
|
||||||
# empty geometry is a valid geometry
|
submitted_data = self.data["output"]
|
||||||
self.cleaned_data["output"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
|
submitted_data = json.loads(submitted_data)
|
||||||
return is_valid
|
submitted_data = self._set_geojson_properties(submitted_data)
|
||||||
geom = json.loads(geom)
|
self.initialize_form_field("output", json.dumps(submitted_data))
|
||||||
|
|
||||||
# Write submitted data back into form field to make sure invalid geometry
|
# Get geojson from form for validity checking
|
||||||
# will be rendered again on failed submit
|
geom = self.data.get("output", json.dumps({}))
|
||||||
self.initialize_form_field("output", self.data["output"])
|
geom = json.loads(geom)
|
||||||
|
|
||||||
# Initialize features list with empty MultiPolygon, so that an empty input will result in a
|
# Initialize features list with empty MultiPolygon, so that an empty input will result in a
|
||||||
# proper empty MultiPolygon object
|
# proper empty MultiPolygon object
|
||||||
@@ -84,20 +85,23 @@ class SimpleGeomForm(BaseForm):
|
|||||||
"MultiPolygon",
|
"MultiPolygon",
|
||||||
"MultiPolygon25D",
|
"MultiPolygon25D",
|
||||||
]
|
]
|
||||||
|
# Check validity for each feature of the geometry
|
||||||
for feature in features_json:
|
for feature in features_json:
|
||||||
feature_geom = feature.get("geometry", feature)
|
feature_geom = feature.get("geometry", feature)
|
||||||
if feature_geom is None:
|
if feature_geom is None:
|
||||||
# Fallback for rare cases where a feature does not contain any geometry
|
# Fallback for rare cases where a feature does not contain any geometry
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Try to create a geometry object from the single feature
|
||||||
feature_geom = json.dumps(feature_geom)
|
feature_geom = json.dumps(feature_geom)
|
||||||
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
flatten_geometry = g.coord_dim > 2
|
geometry_has_unwanted_dimensions = g.coord_dim > 2
|
||||||
if flatten_geometry:
|
if geometry_has_unwanted_dimensions:
|
||||||
g = self.__flatten_geom_to_2D(g)
|
g = self.__flatten_geom_to_2D(g)
|
||||||
|
|
||||||
if g.geom_type not in accepted_ogr_types:
|
geometry_type_is_accepted = g.geom_type not in accepted_ogr_types
|
||||||
|
if geometry_type_is_accepted:
|
||||||
self.add_error("output", _("Only surfaces allowed. Points or lines must be buffered."))
|
self.add_error("output", _("Only surfaces allowed. Points or lines must be buffered."))
|
||||||
is_valid &= False
|
is_valid &= False
|
||||||
return is_valid
|
return is_valid
|
||||||
@@ -109,27 +113,33 @@ class SimpleGeomForm(BaseForm):
|
|||||||
self._num_geometries_ignored += 1
|
self._num_geometries_ignored += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Whatever this geometry object is -> try to create a Polygon from it
|
||||||
|
# The resulting polygon object automatically detects whether a valid polygon has been created or not
|
||||||
g = Polygon.from_ewkt(g.ewkt)
|
g = Polygon.from_ewkt(g.ewkt)
|
||||||
is_valid &= g.valid
|
is_valid &= g.valid
|
||||||
if not g.valid:
|
if not g.valid:
|
||||||
self.add_error("output", g.valid_reason)
|
self.add_error("output", g.valid_reason)
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
|
# If the resulting polygon is just a single polygon, we add it to the list of properly casted features
|
||||||
if isinstance(g, Polygon):
|
if isinstance(g, Polygon):
|
||||||
features.append(g)
|
features.append(g)
|
||||||
elif isinstance(g, MultiPolygon):
|
elif isinstance(g, MultiPolygon):
|
||||||
|
# The resulting polygon could be of type MultiPolygon (due to multiple surfaces)
|
||||||
|
# If so, we extract all polygons from the MultiPolygon and extend the casted features list
|
||||||
features.extend(list(g))
|
features.extend(list(g))
|
||||||
|
|
||||||
# Unionize all geometry features into one new MultiPolygon
|
# Unionize all polygon features into one new MultiPolygon
|
||||||
if features:
|
if features:
|
||||||
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
|
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
|
||||||
else:
|
else:
|
||||||
|
# If no features have been processed, this indicates an empty geometry - so we store an empty geometry
|
||||||
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
||||||
form_geom = Geometry.cast_to_multipolygon(form_geom)
|
form_geom = Geometry.cast_to_multipolygon(form_geom)
|
||||||
|
|
||||||
# Write unioned Multipolygon into cleaned data
|
# Write unionized Multipolygon back into cleaned data
|
||||||
if self.cleaned_data is None:
|
if self.cleaned_data is None:
|
||||||
self.cleaned_data = {}
|
self.cleaned_data = {}
|
||||||
self.cleaned_data["output"] = form_geom.ewkt
|
self.cleaned_data["output"] = form_geom.ewkt
|
||||||
@@ -252,6 +262,8 @@ class SimpleGeomForm(BaseForm):
|
|||||||
"""
|
"""
|
||||||
features = geojson.get("features", [])
|
features = geojson.get("features", [])
|
||||||
for feature in features:
|
for feature in features:
|
||||||
|
if not feature.get("properties", None):
|
||||||
|
feature["properties"] = {}
|
||||||
feature["properties"]["editable"] = not self.read_only
|
feature["properties"]["editable"] = not self.read_only
|
||||||
if title:
|
if title:
|
||||||
feature["properties"]["title"] = title
|
feature["properties"]["title"] = title
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import json
|
|||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib.gis.geos import MultiPolygon
|
from django.contrib.gis.geos import MultiPolygon
|
||||||
|
|
||||||
@@ -109,17 +110,26 @@ class Geometry(BaseResource):
|
|||||||
objs (list): The list of objects
|
objs (list): The list of objects
|
||||||
"""
|
"""
|
||||||
objs = []
|
objs = []
|
||||||
sets = [
|
|
||||||
|
# Some related data sets can be processed rather easily
|
||||||
|
regular_sets = [
|
||||||
self.intervention_set,
|
self.intervention_set,
|
||||||
self.compensation_set,
|
|
||||||
self.ema_set,
|
self.ema_set,
|
||||||
self.ecoaccount_set,
|
self.ecoaccount_set,
|
||||||
]
|
]
|
||||||
for _set in sets:
|
for _set in regular_sets:
|
||||||
set_objs = _set.filter(
|
set_objs = _set.filter(
|
||||||
deleted=None
|
deleted=None
|
||||||
)
|
)
|
||||||
objs += set_objs
|
objs += set_objs
|
||||||
|
|
||||||
|
# ... but we need a special treatment for compensations, since they can be deleted directly OR inherit their
|
||||||
|
# de-facto-deleted status from their deleted parent intervention
|
||||||
|
comp_objs = self.compensation_set.filter(
|
||||||
|
Q(deleted=None) & Q(intervention__deleted=None)
|
||||||
|
)
|
||||||
|
objs += comp_objs
|
||||||
|
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
def get_data_object(self):
|
def get_data_object(self):
|
||||||
|
|||||||
@@ -677,12 +677,12 @@ class GeoReferencedMixin(models.Model):
|
|||||||
return request
|
return request
|
||||||
|
|
||||||
instance_objs = []
|
instance_objs = []
|
||||||
conflicts = self.geometry.conflicts_geometries.all()
|
conflicts = self.geometry.conflicts_geometries.iterator()
|
||||||
|
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.affected_geometry.get_data_objects()
|
instance_objs += conflict.affected_geometry.get_data_objects()
|
||||||
|
|
||||||
conflicts = self.geometry.conflicted_by_geometries.all()
|
conflicts = self.geometry.conflicted_by_geometries.iterator()
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.conflicting_geometry.get_data_objects()
|
instance_objs += conflict.conflicting_geometry.get_data_objects()
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ BASE_TITLE = "KSP - Kompensationsverzeichnis Service Portal"
|
|||||||
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
|
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
|
||||||
TAB_TITLE_IDENTIFIER = "tab_title"
|
TAB_TITLE_IDENTIFIER = "tab_title"
|
||||||
HELP_LINK = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp2:start"
|
HELP_LINK = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp2:start"
|
||||||
IMPRESSUM_LINK = "https://naturschutz.rlp.de/index.php?q=impressum"
|
IMPRESSUM_LINK = "https://naturschutz.rlp.de/ueber-uns/impressum"
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if geom_form.geom.errors %}
|
{% if geom_form.output.errors %}
|
||||||
<div class="alert-danger p-2">
|
<div class="alert-danger p-2">
|
||||||
{% for error in geom_form.geom.errors %}
|
{% for error in geom_form.output.errors %}
|
||||||
<strong class="invalid">{{ error }}</strong>
|
<strong class="invalid">{{ error }}</strong>
|
||||||
<br>
|
<br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user