add comments and fix performance of altitude area calculation

This commit is contained in:
Laura Klünder 2019-12-20 18:38:13 +01:00
parent f8e5e4dc31
commit d15d5ac76c

View file

@ -1,4 +1,5 @@
import logging import logging
from collections import deque
from decimal import Decimal from decimal import Decimal
from itertools import chain, combinations from itertools import chain, combinations
from operator import attrgetter, itemgetter from operator import attrgetter, itemgetter
@ -319,6 +320,8 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
areas_without_altitude = set(area.tmpid for area in areas if area.altitude is None) areas_without_altitude = set(area.tmpid for area in areas if area.altitude is None)
# interpolate altitudes # interpolate altitudes
logger.info('Interpolating altitudes...')
areas_with_altitude = [i for i in range(len(areas)) if i not in areas_without_altitude] areas_with_altitude = [i for i in range(len(areas)) if i not in areas_without_altitude]
for i, tmpid in enumerate(areas_with_altitude): for i, tmpid in enumerate(areas_with_altitude):
areas[tmpid].i = i areas[tmpid].i = i
@ -445,9 +448,12 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
level_areas[ramp.level].append(ramp) level_areas[ramp.level].append(ramp)
# #
# now fill in the obstacles and so on # we have altitude areas, but they only cover accessible space for now
# however, we need obstacles to be part of altitude areas too.
# this is where we do that.
# #
for level in levels: for level in levels:
logger.info('Assign remaining space (%s)...' % level.title)
for space in level.spaces.all(): for space in level.spaces.all():
space.geometry = space.orig_geometry space.geometry = space.orig_geometry
@ -455,16 +461,20 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
doors_geom = unary_union(tuple(d.geometry for d in level.doors.all())) doors_geom = unary_union(tuple(d.geometry for d in level.doors.all()))
space_geom = unary_union(tuple((s.geometry if not s.outside else s.geometry.difference(buildings_geom)) space_geom = unary_union(tuple((s.geometry if not s.outside else s.geometry.difference(buildings_geom))
for s in level.spaces.all())) for s in level.spaces.all()))
# accessible area on this level is doors + spaces - holes
accessible_area = unary_union((doors_geom, space_geom)) accessible_area = unary_union((doors_geom, space_geom))
for space in level.spaces.all(): for space in level.spaces.all():
accessible_area = accessible_area.difference(space.geometry.intersection( accessible_area = accessible_area.difference(space.geometry.intersection(
unary_union(tuple(h.geometry for h in space.holes.all())) unary_union(tuple(h.geometry for h in space.holes.all()))
)) ))
# areas mean altitude areas (including ramps) here
our_areas = level_areas.get(level, []) our_areas = level_areas.get(level, [])
for area in our_areas: for area in our_areas:
area.orig_geometry = area.geometry area.orig_geometry = area.geometry
area.orig_geometry_prep = prepared.prep(area.geometry) area.orig_geometry_prep = prepared.prep(area.geometry)
area.polygons_to_add = deque()
stairs = [] stairs = []
for space in level.spaces.all(): for space in level.spaces.all():
@ -473,17 +483,22 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
space_geom = space_geom.difference(buildings_geom) space_geom = space_geom.difference(buildings_geom)
space_geom_prep = prepared.prep(space_geom) space_geom_prep = prepared.prep(space_geom)
holes_geom = unary_union(tuple(h.geometry for h in space.holes.all())) holes_geom = unary_union(tuple(h.geometry for h in space.holes.all()))
# remaining_space means remaining space (=obstacles) that still needs to be added to altitude areas
remaining_space = ( remaining_space = (
tuple(o.geometry for o in space.obstacles.all()) + tuple(o.geometry for o in space.obstacles.all()) +
tuple(o.buffered_geometry for o in space.lineobstacles.all()) tuple(o.buffered_geometry for o in space.lineobstacles.all())
) )
# make sure to remove everything outside the space the obstacles are in as well as holes
remaining_space = tuple(g.intersection(space_geom).difference(holes_geom) remaining_space = tuple(g.intersection(space_geom).difference(holes_geom)
for g in remaining_space for g in remaining_space
if space_geom_prep.intersects(g)) if space_geom_prep.intersects(g))
# we need this to be a list of simple normal polygons
remaining_space = tuple(chain(*( remaining_space = tuple(chain(*(
assert_multipolygon(g) for g in remaining_space if not g.is_empty assert_multipolygon(g) for g in remaining_space if not g.is_empty
))) )))
if not remaining_space: if not remaining_space:
# if there are no remaining spaces? great, we're done here.
continue continue
cuts = [] cuts = []
@ -526,7 +541,12 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
else: else:
area = min(our_areas, area = min(our_areas,
key=lambda a: a.orig_geometry.distance(center)-(0 if a.altitude2 is None else 0.6)) key=lambda a: a.orig_geometry.distance(center)-(0 if a.altitude2 is None else 0.6))
area.geometry = area.geometry.buffer(0).union(polygon) area.polygons_to_add.append(polygon)
for i_area, area in enumerate(our_areas):
if area.polygons_to_add:
area.geometry = unary_union((area.geometry.buffer(0), *area.polygons_to_add))
del area.polygons_to_add
for level in levels: for level in levels:
level_areas[level] = set(area.tmpid for area in level_areas.get(level, [])) level_areas[level] = set(area.tmpid for area in level_areas.get(level, []))