#50 Overlaying geometries
* refactors geometry field into GeoReferencedMixin, holding more handy methods and used in all models, formerly holding the geometry field * refactors backend admin configuration, so modified, deleted and created are not editable in the backend which also skips loading of all possible choices * fixes typo in sanitize_db command * introduces GeometryConflict model, holding a link between two geometries, where one overlaps the other * adds first (WIP) messages into detail views of ema and intervention for test purposes
This commit is contained in:
@@ -6,13 +6,93 @@ Created on: 15.11.21
|
||||
|
||||
"""
|
||||
from django.contrib.gis.db.models import MultiPolygonField
|
||||
from django.db import models
|
||||
|
||||
from konova.models import BaseResource
|
||||
from konova.models import BaseResource, UuidModel
|
||||
|
||||
|
||||
class Geometry(BaseResource):
|
||||
"""
|
||||
Outsourced geometry model so multiple versions of the same object can refer to the same geometry if it is not changed
|
||||
Geometry model
|
||||
"""
|
||||
from konova.settings import DEFAULT_SRID
|
||||
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
|
||||
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
self.check_for_conflicts()
|
||||
|
||||
def check_for_conflicts(self):
|
||||
""" Checks for geometry overlaps
|
||||
|
||||
Creates a new GeometryConflict entry for each overlap to another geometry, which has already been there before
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# If no geometry is given or important data is missing, we can not perform any checks
|
||||
if self.geom is None or (self.created is None and self.modified is None):
|
||||
return None
|
||||
|
||||
check_timestamp_obj = self.modified or self.created
|
||||
ts = check_timestamp_obj.timestamp
|
||||
overlapping_geoms = Geometry.objects.filter(
|
||||
modified__timestamp__lte=ts,
|
||||
geom__overlaps=self.geom,
|
||||
)
|
||||
|
||||
# Drop known conflicts for this object to replace with new ones
|
||||
self.conflicts_geometries.all().delete()
|
||||
for match in overlapping_geoms:
|
||||
GeometryConflict.objects.get_or_create(conflicting_geometry=self, existing_geometry=match)
|
||||
|
||||
# Rerun the conflict check for all conflicts where this object is not the cause but the one that already existed.
|
||||
# It may be possible that this object has been edited, so the conflicts would be resolved in the newer entries.
|
||||
existing_conflicts = self.conflicted_by_geometries.all()
|
||||
for conflict in existing_conflicts:
|
||||
conflicting_geom = conflict.conflicting_geometry
|
||||
conflicting_geom.check_for_conflicts()
|
||||
|
||||
def get_data_objects(self):
|
||||
""" Getter for all objects which are related to this geometry
|
||||
|
||||
Returns:
|
||||
objs (list): The list of objects
|
||||
"""
|
||||
objs = []
|
||||
sets = [
|
||||
self.intervention_set,
|
||||
self.compensation_set,
|
||||
self.ema_set,
|
||||
self.ecoaccount_set,
|
||||
]
|
||||
for _set in sets:
|
||||
set_objs = _set.filter(
|
||||
deleted=None
|
||||
)
|
||||
objs += set_objs
|
||||
return objs
|
||||
|
||||
|
||||
class GeometryConflict(UuidModel):
|
||||
"""
|
||||
Geometry conflicts model
|
||||
|
||||
If a new/edited geometry overlays an existing geometry, there will be a new GeometryConflict on the db
|
||||
"""
|
||||
conflicting_geometry = models.ForeignKey(
|
||||
Geometry,
|
||||
on_delete=models.CASCADE,
|
||||
help_text="The geometry which came second",
|
||||
related_name="conflicts_geometries"
|
||||
)
|
||||
existing_geometry = models.ForeignKey(
|
||||
Geometry,
|
||||
on_delete=models.CASCADE,
|
||||
help_text="The geometry which came first",
|
||||
related_name="conflicted_by_geometries"
|
||||
)
|
||||
detected_on = models.DateTimeField(auto_now_add=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.conflicting_geometry.id} conflicts with {self.existing_geometry.id}"
|
||||
|
||||
Reference in New Issue
Block a user