Merge branch 'master' into 132_Old_entries

# Conflicts:
#	konova/models/geometry.py
This commit is contained in:
mpeltriaux 2022-11-24 06:59:05 +01:00
commit 4f5c172449
24 changed files with 138 additions and 70 deletions

View File

@ -1,5 +1,5 @@
{ {
"eco_account": "CHANGE_BEFORE_RUN!!!", "eco_account": "CHANGE_BEFORE_RUN!!!",
"surface": 500.0, "surface": 500.50,
"intervention": "CHANGE_BEFORE_RUN!!!" "intervention": "CHANGE_BEFORE_RUN!!!"
} }

View File

@ -1,5 +1,5 @@
{ {
"eco_account": "CHANGE_BEFORE_RUN!!!", "eco_account": "CHANGE_BEFORE_RUN!!!",
"surface": 523400.0, "surface": 523400.50,
"intervention": "CHANGE_BEFORE_RUN!!!" "intervention": "CHANGE_BEFORE_RUN!!!"
} }

View File

@ -136,8 +136,6 @@ class AbstractModelAPISerializer:
geometry = geos.fromstr(geojson) geometry = geos.fromstr(geojson)
if geometry.srid != DEFAULT_SRID_RLP: if geometry.srid != DEFAULT_SRID_RLP:
geometry.transform(DEFAULT_SRID_RLP) geometry.transform(DEFAULT_SRID_RLP)
if geometry.empty:
geometry = None
return geometry return geometry
def _get_obj_from_db(self, id, user): def _get_obj_from_db(self, id, user):

View File

@ -11,7 +11,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, Abs
from compensation.models import Compensation from compensation.models import Compensation
from intervention.models import Intervention from intervention.models import Intervention
from konova.models import Geometry from konova.models import Geometry
from konova.tasks import celery_update_parcels from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from konova.utils.message_templates import DATA_UNSHARED from konova.utils.message_templates import DATA_UNSHARED
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -128,6 +128,7 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
obj.log.add(obj.created) obj.log.add(obj.created)
celery_update_parcels.delay(obj.geometry.id) celery_update_parcels.delay(obj.geometry.id)
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
return obj.id return obj.id

View File

@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
from compensation.models import EcoAccount from compensation.models import EcoAccount
from intervention.models import Legal, Responsibility, Handler from intervention.models import Legal, Responsibility, Handler
from konova.models import Geometry from konova.models import Geometry
from konova.tasks import celery_update_parcels from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -150,6 +150,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
obj.users.add(user) obj.users.add(user)
celery_update_parcels.delay(obj.geometry.id) celery_update_parcels.delay(obj.geometry.id)
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
return obj.id return obj.id

View File

@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
from ema.models import Ema from ema.models import Ema
from intervention.models import Responsibility, Handler from intervention.models import Responsibility, Handler
from konova.models import Geometry from konova.models import Geometry
from konova.tasks import celery_update_parcels from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -122,6 +122,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
obj.users.add(user) obj.users.add(user)
celery_update_parcels.delay(obj.geometry.id) celery_update_parcels.delay(obj.geometry.id)
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
return obj.id return obj.id

View File

@ -13,7 +13,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, \
from compensation.models import Payment from compensation.models import Payment
from intervention.models import Intervention, Responsibility, Legal, Handler from intervention.models import Intervention, Responsibility, Legal, Handler
from konova.models import Geometry from konova.models import Geometry
from konova.tasks import celery_update_parcels from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -165,6 +165,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
obj.log.add(obj.created) obj.log.add(obj.created)
celery_update_parcels.delay(obj.geometry.id) celery_update_parcels.delay(obj.geometry.id)
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
return obj.id return obj.id

View File

@ -392,7 +392,8 @@ class AbstractCompensationAPISerializerV1Mixin:
self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"] self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"]
] ]
amount = float(entry["amount"]) amount = float(entry["amount"])
unit = entry["unit"] # Mapping of old "qm" into "m²"
unit = UnitChoices.m2.value if entry["unit"] == "qm" else entry["unit"]
comment = entry["comment"] comment = entry["comment"]
# Check on validity # Check on validity

View File

@ -129,12 +129,11 @@ class NewCompensationForm(AbstractCompensationForm,
self.initialize_form_field("identifier", identifier) self.initialize_form_field("identifier", identifier)
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id") self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
def __create_comp(self, user, geom_form) -> Compensation: def __create_comp(self, user):
""" Creates the compensation from form data """ Creates the compensation from form data
Args: Args:
user (User): The performing user user (User): The performing user
geom_form (SimpleGeomForm): The geometry form
Returns: Returns:
comp (Compensation): The compensation object comp (Compensation): The compensation object
@ -150,8 +149,6 @@ class NewCompensationForm(AbstractCompensationForm,
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(user) action = UserActionLogEntry.get_created_action(user)
# Process the geometry form
geometry = geom_form.save(action)
# Finally create main object # Finally create main object
comp = Compensation.objects.create( comp = Compensation.objects.create(
@ -162,18 +159,23 @@ class NewCompensationForm(AbstractCompensationForm,
is_cef=is_cef, is_cef=is_cef,
is_coherence_keeping=is_coherence_keeping, is_coherence_keeping=is_coherence_keeping,
is_pik=is_pik, is_pik=is_pik,
geometry=geometry,
comment=comment, comment=comment,
) )
# Add the log entry to the main objects log list # Add the log entry to the main objects log list
comp.log.add(action) comp.log.add(action)
return comp return comp, action
def save(self, user: User, geom_form: SimpleGeomForm): def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic(): with transaction.atomic():
comp = self.__create_comp(user, geom_form) comp, action = self.__create_comp(user)
comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier)) comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
# Process the geometry form
geometry = geom_form.save(action)
comp.geometry = geometry
comp.save()
return comp return comp
@ -205,6 +207,9 @@ class EditCompensationForm(NewCompensationForm):
def save(self, user: User, geom_form: SimpleGeomForm): def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic(): with transaction.atomic():
# Create log entry
action = UserActionLogEntry.get_edited_action(user)
# Fetch data from cleaned POST values # Fetch data from cleaned POST values
identifier = self.cleaned_data.get("identifier", None) identifier = self.cleaned_data.get("identifier", None)
title = self.cleaned_data.get("title", None) title = self.cleaned_data.get("title", None)
@ -214,17 +219,9 @@ class EditCompensationForm(NewCompensationForm):
is_pik = self.cleaned_data.get("is_pik", None) is_pik = self.cleaned_data.get("is_pik", None)
comment = self.cleaned_data.get("comment", None) comment = self.cleaned_data.get("comment", None)
# Create log entry
action = UserActionLogEntry.get_edited_action(user)
# Process the geometry form
geometry = geom_form.save(action)
# Finally create main object
self.instance.identifier = identifier self.instance.identifier = identifier
self.instance.title = title self.instance.title = title
self.instance.intervention = intervention self.instance.intervention = intervention
self.instance.geometry = geometry
self.instance.is_cef = is_cef self.instance.is_cef = is_cef
self.instance.is_coherence_keeping = is_coherence_keeping self.instance.is_coherence_keeping = is_coherence_keeping
self.instance.comment = comment self.instance.comment = comment
@ -233,6 +230,11 @@ class EditCompensationForm(NewCompensationForm):
self.instance.save() self.instance.save()
self.instance.log.add(action) self.instance.log.add(action)
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA) intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
return self.instance
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(action)
self.instance.geometry = geometry
self.instance.save()
return self.instance

View File

@ -94,8 +94,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(user) action = UserActionLogEntry.get_created_action(user)
# Process the geometry form
geometry = geom_form.save(action)
handler = Handler.objects.create( handler = Handler.objects.create(
type=handler_type, type=handler_type,
@ -119,7 +117,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
responsible=responsible, responsible=responsible,
deductable_surface=surface, deductable_surface=surface,
created=action, created=action,
geometry=geometry,
comment=comment, comment=comment,
is_pik=is_pik, is_pik=is_pik,
legal=legal legal=legal
@ -129,6 +126,10 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
# Add the log entry to the main objects log list # Add the log entry to the main objects log list
acc.log.add(action) acc.log.add(action)
# Process the geometry form
geometry = geom_form.save(action)
acc.geometry = geometry
acc.save()
acc.update_deductable_rest() acc.update_deductable_rest()
return acc return acc
@ -185,9 +186,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
# Create log entry # Create log entry
action = UserActionLogEntry.get_edited_action(user) action = UserActionLogEntry.get_edited_action(user)
# Process the geometry form
geometry = geom_form.save(action)
# Update responsible data # Update responsible data
self.instance.responsible.handler.type = handler_type self.instance.responsible.handler.type = handler_type
self.instance.responsible.handler.detail = handler_detail self.instance.responsible.handler.detail = handler_detail
@ -204,7 +202,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
self.instance.identifier = identifier self.instance.identifier = identifier
self.instance.title = title self.instance.title = title
self.instance.deductable_surface = surface self.instance.deductable_surface = surface
self.instance.geometry = geometry
self.instance.comment = comment self.instance.comment = comment
self.instance.is_pik = is_pik self.instance.is_pik = is_pik
self.instance.modified = action self.instance.modified = action
@ -213,6 +210,10 @@ class EditEcoAccountForm(NewEcoAccountForm):
# Add the log entry to the main objects log list # Add the log entry to the main objects log list
self.instance.log.add(action) self.instance.log.add(action)
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(action)
self.instance.geometry = geometry
self.instance.save()
self.instance.update_deductable_rest() self.instance.update_deductable_rest()
return self.instance return self.instance

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.3 on 2022-11-18 15:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compensation', '0013_auto_20221117_0819'),
]
operations = [
migrations.AlterField(
model_name='compensationaction',
name='unit',
field=models.CharField(blank=True, choices=[('cm', 'cm'), ('m', 'm'), ('m2', ''), ('m3', ''), ('km', 'km'), ('ha', 'ha'), ('pcs', 'Pieces')], max_length=100, null=True),
),
]

View File

@ -118,8 +118,15 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
intervention__deleted=None, intervention__deleted=None,
) )
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0 deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
ret_val = available_surfaces - deductions_surfaces available_surface = self.deductable_surface
if available_surface is None:
# Fallback!
available_surface = deductions_surfaces
else:
available_surface = float(available_surface)
ret_val = available_surface - deductions_surfaces
return ret_val return ret_val

View File

@ -188,7 +188,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
# Prepare data for deduction creation # Prepare data for deduction creation
deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,)) deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,))
test_surface = 10.00 test_surface = 10.50
post_data = { post_data = {
"surface": test_surface, "surface": test_surface,
"account": self.eco_account.id, "account": self.eco_account.id,
@ -207,7 +207,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
# Make sure the deductible surface is valid for the request # Make sure the deductible surface is valid for the request
self.eco_account.set_recorded(self.superuser) self.eco_account.set_recorded(self.superuser)
self.eco_account.refresh_from_db() self.eco_account.refresh_from_db()
self.eco_account.deductable_surface = test_surface + 1.00 self.eco_account.deductable_surface = test_surface + 1.0
self.eco_account.save() self.eco_account.save()
self.assertIsNotNone(self.eco_account.recorded) self.assertIsNotNone(self.eco_account.recorded)
self.assertGreater(self.eco_account.deductable_surface, test_surface) self.assertGreater(self.eco_account.deductable_surface, test_surface)
@ -244,7 +244,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
deduction = EcoAccountDeduction.objects.create( deduction = EcoAccountDeduction.objects.create(
intervention=self.intervention, intervention=self.intervention,
account=self.eco_account, account=self.eco_account,
surface=0 surface=1.10
) )
self.assertEqual(1, self.intervention.deductions.count()) self.assertEqual(1, self.intervention.deductions.count())
self.assertEqual(1, self.eco_account.deductions.count()) self.assertEqual(1, self.eco_account.deductions.count())

View File

@ -64,8 +64,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(user) action = UserActionLogEntry.get_created_action(user)
# Process the geometry form
geometry = geom_form.save(action)
handler = Handler.objects.create( handler = Handler.objects.create(
type=handler_type, type=handler_type,
@ -83,7 +81,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
title=title, title=title,
responsible=responsible, responsible=responsible,
created=action, created=action,
geometry=geometry,
comment=comment, comment=comment,
is_pik=is_pik, is_pik=is_pik,
) )
@ -93,6 +90,11 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
# Add the log entry to the main objects log list # Add the log entry to the main objects log list
acc.log.add(action) acc.log.add(action)
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(action)
acc.geometry = geometry
acc.save()
return acc return acc
@ -141,8 +143,6 @@ class EditEmaForm(NewEmaForm):
# Create log entry # Create log entry
action = UserActionLogEntry.get_edited_action(user) action = UserActionLogEntry.get_edited_action(user)
# Process the geometry form
geometry = geom_form.save(action)
# Update responsible data # Update responsible data
self.instance.responsible.handler.type = handler_type self.instance.responsible.handler.type = handler_type
@ -155,7 +155,6 @@ class EditEmaForm(NewEmaForm):
# Update main oject data # Update main oject data
self.instance.identifier = identifier self.instance.identifier = identifier
self.instance.title = title self.instance.title = title
self.instance.geometry = geometry
self.instance.comment = comment self.instance.comment = comment
self.instance.is_pik = is_pik self.instance.is_pik = is_pik
self.instance.modified = action self.instance.modified = action
@ -163,6 +162,11 @@ class EditEmaForm(NewEmaForm):
# Add the log entry to the main objects log list # Add the log entry to the main objects log list
self.instance.log.add(action) self.instance.log.add(action)
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(action)
self.instance.geometry = geometry
self.instance.save()
return self.instance return self.instance

View File

@ -263,9 +263,6 @@ class NewInterventionForm(BaseForm):
handler=handler, handler=handler,
) )
# Process the geometry form
geometry = geom_form.save(action)
# Finally create main object, holding the other objects # Finally create main object, holding the other objects
intervention = Intervention.objects.create( intervention = Intervention.objects.create(
identifier=identifier, identifier=identifier,
@ -273,7 +270,6 @@ class NewInterventionForm(BaseForm):
responsible=responsibility_data, responsible=responsibility_data,
legal=legal_data, legal=legal_data,
created=action, created=action,
geometry=geometry,
comment=comment, comment=comment,
) )
@ -282,6 +278,12 @@ class NewInterventionForm(BaseForm):
# Add the performing user as the first user having access to the data # Add the performing user as the first user having access to the data
intervention.share_with_user(user) intervention.share_with_user(user)
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(action)
intervention.geometry = geometry
intervention.save()
return intervention return intervention
@ -370,9 +372,6 @@ class EditInterventionForm(NewInterventionForm):
user_action = self.instance.mark_as_edited(user, edit_comment=EDITED_GENERAL_DATA) user_action = self.instance.mark_as_edited(user, edit_comment=EDITED_GENERAL_DATA)
geometry = geom_form.save(user_action)
self.instance.geometry = geometry
self.instance.log.add(user_action) self.instance.log.add(user_action)
self.instance.identifier = identifier self.instance.identifier = identifier
@ -381,5 +380,10 @@ class EditInterventionForm(NewInterventionForm):
self.instance.modified = user_action self.instance.modified = user_action
self.instance.save() self.instance.save()
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
geometry = geom_form.save(user_action)
self.instance.geometry = geometry
self.instance.save()
return self.instance return self.instance

View File

@ -15,7 +15,7 @@ from django.utils.translation import gettext_lazy as _
from konova.forms.base_form import BaseForm from konova.forms.base_form import BaseForm
from konova.models import Geometry from konova.models import Geometry
from konova.tasks import celery_update_parcels from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -63,6 +63,7 @@ class SimpleGeomForm(BaseForm):
geom = self.data["geom"] geom = self.data["geom"]
if geom is None or len(geom) == 0: if geom is None or len(geom) == 0:
# empty geometry is a valid geometry # empty geometry is a valid geometry
self.cleaned_data["geom"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
return is_valid return is_valid
geom = json.loads(geom) geom = json.loads(geom)
@ -82,7 +83,12 @@ class SimpleGeomForm(BaseForm):
"MultiPolygon25D", "MultiPolygon25D",
] ]
for feature in features_json: for feature in features_json:
feature_geom = json.dumps(feature.get("geometry", feature)) feature_geom = feature.get("geometry", feature)
if feature_geom is None:
# Fallback for rare cases where a feature does not contain any geometry
continue
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 flatten_geometry = g.coord_dim > 2
@ -101,6 +107,8 @@ class SimpleGeomForm(BaseForm):
return is_valid return is_valid
features.append(polygon) features.append(polygon)
# Unionize all geometry features into one new MultiPolygon
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP) form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
for feature in features: for feature in features:
form_geom = form_geom.union(feature) form_geom = form_geom.union(feature)
@ -141,8 +149,9 @@ class SimpleGeomForm(BaseForm):
geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID_RLP)), geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID_RLP)),
created=action, created=action,
) )
# Start the parcel update procedure in a background process # Start parcel update and geometry conflict checking procedure in a background process
celery_update_parcels.delay(geometry.id) celery_update_parcels.delay(geometry.id)
celery_check_for_geometry_conflicts.delay(geometry.id)
return geometry return geometry
def __flatten_geom_to_2D(self, geom): def __flatten_geom_to_2D(self, geom):

View File

@ -9,16 +9,15 @@ import json
from time import sleep from time import sleep
from django.contrib.gis.db.models import MultiPolygonField from django.contrib.gis.db.models import MultiPolygonField
from django.contrib.gis.geos import Polygon
from django.core.exceptions import MultipleObjectsReturned from django.core.exceptions import MultipleObjectsReturned
from django.db import models from django.db import models, transaction
from django.utils import timezone from django.utils import timezone
from konova.models import BaseResource, UuidModel from konova.models import BaseResource, UuidModel
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
from konova.utils.wfs.spatial import ParcelWFSFetcher
from konova.utils.mutex import cache_lock from konova.utils.mutex import cache_lock
from konova.tasks import celery_check_for_geometry_conflicts from konova.tasks import celery_check_for_geometry_conflicts
from konova.utils.wfs.spatial import ParcelWFSFetcher
class Geometry(BaseResource): class Geometry(BaseResource):
@ -32,7 +31,18 @@ class Geometry(BaseResource):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
celery_check_for_geometry_conflicts.delay(self.id)
@property
def geom_small_buffered(self):
"""
Returns a smaller buffered version of the geometry.
Can be used to shrink the geometry used for intersection purposes to avoid intersection detection on
neighbouring geometries.
Returns:
"""
return self.geom.buffer(-0.001)
def check_for_conflicts(self): def check_for_conflicts(self):
""" Checks for new geometry overlaps """ Checks for new geometry overlaps
@ -47,9 +57,8 @@ class Geometry(BaseResource):
return None return None
self.recheck_existing_conflicts() self.recheck_existing_conflicts()
overlapping_geoms = Geometry.objects.filter( overlapping_geoms = Geometry.objects.filter(
geom__intersects=self.geom, geom__intersects=self.geom_small_buffered,
).exclude( ).exclude(
id=self.id id=self.id
).distinct() ).distinct()
@ -71,14 +80,14 @@ class Geometry(BaseResource):
""" """
all_conflicts_as_conflicting = self.conflicts_geometries.all() all_conflicts_as_conflicting = self.conflicts_geometries.all()
still_conflicting_conflicts = all_conflicts_as_conflicting.filter( still_conflicting_conflicts = all_conflicts_as_conflicting.filter(
affected_geometry__geom__intersects=self.geom affected_geometry__geom__intersects=self.geom_small_buffered
) )
resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts) resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts)
resolved_conflicts.delete() resolved_conflicts.delete()
all_conflicted_by_conflicts = self.conflicted_by_geometries.all() all_conflicted_by_conflicts = self.conflicted_by_geometries.all()
still_conflicting_conflicts = all_conflicted_by_conflicts.filter( still_conflicting_conflicts = all_conflicted_by_conflicts.filter(
conflicting_geometry__geom__intersects=self.geom conflicting_geometry__geom__intersects=self.geom_small_buffered
) )
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts) resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
resolved_conflicts.delete() resolved_conflicts.delete()
@ -110,6 +119,11 @@ class Geometry(BaseResource):
""" """
from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup
if self.geom.empty:
# Nothing to do
return
parcel_fetcher = ParcelWFSFetcher( parcel_fetcher = ParcelWFSFetcher(
geometry_id=self.id, geometry_id=self.id,
) )

View File

@ -15,7 +15,7 @@ DEFAULT_SRID_RLP = 25832
# Needed to redirect to LANIS # Needed to redirect to LANIS
## Values to be inserted are [zoom_level, x_coord, y_coord] ## Values to be inserted are [zoom_level, x_coord, y_coord]
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz" LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_recorded,eiv_unrecorded,kom_recorded,kom_unrecorded,oek_recorded,oek_unrecorded,ema_recorded,ema_unrecorded,mae&service=kartendienste_naturschutz"
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry. ## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
LANIS_ZOOM_LUT = { LANIS_ZOOM_LUT = {
1000000000: 6, 1000000000: 6,

View File

@ -17,11 +17,17 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
{% if geom_form.instance.geometry %}
<div hx-trigger="load, every 5s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}"> <div hx-trigger="load, every 5s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}">
<div class="row justify-content-center"> <div class="row justify-content-center">
<span class="spinner-border rlp-r-inv" role="status"></span> <span class="spinner-border rlp-r-inv" role="status"></span>
</div> </div>
</div> </div>
{% else %}
<div class="alert alert-danger">
{% translate 'No geometry entry found on database. Please contact an admin!' %}
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -91,7 +91,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
geom = Geometry.objects.filter( geom = Geometry.objects.filter(
id=self.geometry_id id=self.geometry_id
).annotate( ).annotate(
smaller=Func(F('geom'), -0.001, function="ST_Buffer") smaller=Func(F('geom'), -0.001, function="ST_Buffer") # same as geometry.geom_small_buffered but for QuerySet
).annotate( ).annotate(
gml=AsGML(MakeValid('smaller')) gml=AsGML(MakeValid('smaller'))
).first() ).first()

View File

@ -32,16 +32,16 @@ def get_geom_parcels(request: HttpRequest, id: str):
parcels = geom.get_underlying_parcels() parcels = geom.get_underlying_parcels()
geos_geom = geom.geom geos_geom = geom.geom
parcels_are_currently_calculated = geos_geom is not None and geos_geom.area > 0 and len(parcels) == 0 geometry_exists = not geos_geom.empty
parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0
parcels_available = len(parcels) > 0 parcels_available = len(parcels) > 0
no_geometry_given = geos_geom is None
if parcels_are_currently_calculated: if parcels_are_currently_calculated:
# Parcels are being calculated right now. Change the status code, so polling stays active for fetching # Parcels are being calculated right now. Change the status code, so polling stays active for fetching
# resutls after the calculation # resutls after the calculation
status_code = 200 status_code = 200
if parcels_available or no_geometry_given: if parcels_available or not geometry_exists:
parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr") parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
municipals = parcels.order_by("municipal").distinct("municipal").values("municipal__id") municipals = parcels.order_by("municipal").distinct("municipal").values("municipal__id")
municipals = Municipal.objects.filter(id__in=municipals) municipals = Municipal.objects.filter(id__in=municipals)

Binary file not shown.

View File

@ -2307,8 +2307,8 @@ msgid "Server Error"
msgstr "" msgstr ""
#: templates/500.html:10 #: templates/500.html:10
msgid "Something happened. We are working on it!" msgid "Something happened. Admins have been informed. We are working on it!"
msgstr "Irgendetwas ist passiert. Wir arbeiten daran!" msgstr "Irgendetwas ist passiert. Die Administratoren wurden informiert. Wir arbeiten daran!"
#: templates/email/api/verify_token.html:7 #: templates/email/api/verify_token.html:7
msgid "Hello support" msgid "Hello support"

View File

@ -7,7 +7,7 @@
<h1 class="display-4">{% trans 'Server Error' %}</h1> <h1 class="display-4">{% trans 'Server Error' %}</h1>
<hr> <hr>
<p class="lead"> <p class="lead">
{% trans 'Something happened. We are working on it!' %} {% trans 'Something happened. Admins have been informed. We are working on it!' %}
</p> </p>
</div> </div>
{% endblock %} {% endblock %}