#50 Overlaying geometries Tests
* adds test for geometry conflicts * refactors rechecking of existing conflicts to avoid recursion in certain cases * adds/updates translations
This commit is contained in:
@@ -20,7 +20,7 @@ class GeometryAdmin(admin.ModelAdmin):
|
||||
class GeometryConflictAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"conflicting_geometry",
|
||||
"existing_geometry",
|
||||
"affected_geometry",
|
||||
"detected_on",
|
||||
]
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class Geometry(BaseResource):
|
||||
self.check_for_conflicts()
|
||||
|
||||
def check_for_conflicts(self):
|
||||
""" Checks for geometry overlaps
|
||||
""" Checks for new geometry overlaps
|
||||
|
||||
Creates a new GeometryConflict entry for each overlap to another geometry, which has already been there before
|
||||
|
||||
@@ -35,27 +35,40 @@ class Geometry(BaseResource):
|
||||
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
|
||||
self.recheck_existing_conflicts()
|
||||
|
||||
overlapping_geoms = Geometry.objects.filter(
|
||||
Q(modified__timestamp__lte=ts) |
|
||||
Q(created__timestamp__lte=ts),
|
||||
geom__intersects=self.geom,
|
||||
).exclude(
|
||||
id=self.id
|
||||
).distinct()
|
||||
|
||||
# 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)
|
||||
GeometryConflict.objects.get_or_create(conflicting_geometry=self, affected_geometry=match)
|
||||
|
||||
def recheck_existing_conflicts(self):
|
||||
""" Rechecks GeometryConflict entries
|
||||
|
||||
If a conflict seems to be resolved due to no longer intersection between the two geometries, the entry
|
||||
will be deleted.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
all_conflicts_as_conflicting = self.conflicts_geometries.all()
|
||||
still_conflicting_conflicts = all_conflicts_as_conflicting.filter(
|
||||
affected_geometry__geom__intersects=self.geom
|
||||
)
|
||||
resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts)
|
||||
resolved_conflicts.delete()
|
||||
|
||||
all_conflicted_by_conflicts = self.conflicted_by_geometries.all()
|
||||
still_conflicting_conflicts = all_conflicted_by_conflicts.filter(
|
||||
conflicting_geometry__geom__intersects=self.geom
|
||||
)
|
||||
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
||||
resolved_conflicts.delete()
|
||||
|
||||
# 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
|
||||
@@ -90,7 +103,7 @@ class GeometryConflict(UuidModel):
|
||||
help_text="The geometry which came second",
|
||||
related_name="conflicts_geometries"
|
||||
)
|
||||
existing_geometry = models.ForeignKey(
|
||||
affected_geometry = models.ForeignKey(
|
||||
Geometry,
|
||||
on_delete=models.CASCADE,
|
||||
help_text="The geometry which came first",
|
||||
@@ -99,4 +112,4 @@ class GeometryConflict(UuidModel):
|
||||
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}"
|
||||
return f"{self.conflicting_geometry.id} conflicts with {self.affected_geometry.id}"
|
||||
|
||||
@@ -425,7 +425,7 @@ class GeoReferencedMixin(models.Model):
|
||||
add_message = False
|
||||
conflicts = self.geometry.conflicts_geometries.all()
|
||||
for conflict in conflicts:
|
||||
instance_objs += conflict.existing_geometry.get_data_objects()
|
||||
instance_objs += conflict.affected_geometry.get_data_objects()
|
||||
add_message = True
|
||||
conflicts = self.geometry.conflicted_by_geometries.all()
|
||||
for conflict in conflicts:
|
||||
|
||||
49
konova/tests/test_geometries.py
Normal file
49
konova/tests/test_geometries.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 15.12.21
|
||||
|
||||
"""
|
||||
from django.contrib.gis.db.models.functions import Translate
|
||||
|
||||
from konova.models import Geometry, GeometryConflict
|
||||
from konova.tests.test_views import BaseTestCase
|
||||
from user.models import UserActionLogEntry
|
||||
|
||||
|
||||
class GeometryTestCase(BaseTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super().setUpTestData()
|
||||
geom = cls.create_dummy_geometry()
|
||||
action = UserActionLogEntry.get_created_action(cls.superuser)
|
||||
cls.geom_1 = Geometry.objects.create(
|
||||
geom=geom,
|
||||
created=action,
|
||||
)
|
||||
cls.geom_2 = Geometry.objects.create(
|
||||
geom=geom,
|
||||
created=action,
|
||||
)
|
||||
|
||||
def test_geometry_conflict(self):
|
||||
""" Tests whether a geometry conflict will be present in case of identical/overlaying geometries and
|
||||
if the conflict will be resolved if one geometry is edited.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.geom_1.check_for_conflicts()
|
||||
conflict = GeometryConflict.objects.all().first()
|
||||
self.assertEqual(conflict.conflicting_geometry, self.geom_2)
|
||||
self.assertEqual(conflict.affected_geometry, self.geom_1)
|
||||
|
||||
# Move geom_2 somewhere else, expect the conflict to be resolved
|
||||
Geometry.objects.filter(id=self.geom_2.id).update(
|
||||
geom=Translate('geom', 100000, 100000)
|
||||
)
|
||||
self.geom_2.refresh_from_db()
|
||||
self.geom_1.check_for_conflicts()
|
||||
num_conflict = GeometryConflict.objects.all().count()
|
||||
self.assertEqual(0, num_conflict)
|
||||
Reference in New Issue
Block a user