* optimizes recalculate_parcels.py command so that only non-empty geometries will be processed * drops test_identifier_generating.py command due to missing usage
89 lines
3.3 KiB
Python
89 lines
3.3 KiB
Python
"""
|
|
Author: Michel Peltriaux
|
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
Created on: 04.01.22
|
|
|
|
"""
|
|
import datetime
|
|
|
|
from django.contrib.gis.db.models.functions import Area
|
|
|
|
from konova.management.commands.setup import BaseKonovaCommand
|
|
from konova.models import Geometry, ParcelIntersection
|
|
|
|
|
|
class Command(BaseKonovaCommand):
|
|
help = "Recalculates parcels for entries with geometry but missing parcel information"
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
"--force-all",
|
|
action="store_true",
|
|
default=False,
|
|
help="If Attribute set, all entries parcels will be recalculated"
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
try:
|
|
self.recalculate_parcels(options)
|
|
except KeyboardInterrupt:
|
|
self._break_line()
|
|
exit(-1)
|
|
|
|
def recalculate_parcels(self, options: dict):
|
|
force_all = options.get("force_all", False)
|
|
|
|
geometry_objects = Geometry.objects.filter(
|
|
geom__isempty=False,
|
|
).exclude(
|
|
geom=None
|
|
)
|
|
|
|
if not force_all:
|
|
# Fetch all intersections
|
|
intersection_objs = ParcelIntersection.objects.filter(
|
|
geometry__in=geometry_objects
|
|
)
|
|
# Just take the geometry ids, which seem to have intersections
|
|
geom_with_intersection_ids = intersection_objs.values_list(
|
|
"geometry__id",
|
|
flat=True
|
|
)
|
|
# ... and resolve into Geometry objects again ...
|
|
intersected_geom_objs = Geometry.objects.filter(
|
|
id__in=geom_with_intersection_ids
|
|
)
|
|
# ... to be able to use the way more efficient difference() function ...
|
|
geometry_objects_ids = geometry_objects.difference(intersected_geom_objs).values_list("id", flat=True)
|
|
# ... so we can resolve these into proper Geometry objects again for further annotation usage
|
|
geometry_objects = Geometry.objects.filter(id__in=geometry_objects_ids)
|
|
|
|
self._write_warning("=== Update parcels and districts ===")
|
|
# Order geometries by size to process smaller once at first
|
|
geometries = geometry_objects.annotate(
|
|
area=Area("geom")
|
|
).order_by(
|
|
'area'
|
|
)
|
|
self._write_warning(f"Process parcels for {geometries.count()} geometry entries now ...")
|
|
i = 0
|
|
num_geoms = geometries.count()
|
|
geoms_with_errors = {}
|
|
for geometry in geometries:
|
|
self._write_warning(f"--- {datetime.datetime.now()} Process {geometry.id} now ...")
|
|
try:
|
|
geometry.update_parcels()
|
|
self._write_warning(f"--- Processed {geometry.get_underlying_parcels().count()} underlying parcels")
|
|
except Exception as e:
|
|
geoms_with_errors[geometry.id] = str(e)
|
|
i += 1
|
|
self._write_warning(f"--- {i}/{num_geoms} processed")
|
|
|
|
self._write_success("Updating parcels done!")
|
|
|
|
for key, val in geoms_with_errors.items():
|
|
self._write_error(f" Error on {key}: {val}")
|
|
self._write_success(f"{num_geoms - len(geoms_with_errors)} geometries successfuly recalculated!")
|
|
self._break_line()
|