simplify altitudearea recalculate code

This commit is contained in:
Laura Klünder 2017-11-13 19:36:11 +01:00
parent 53af5d59ad
commit 0253d378ed
2 changed files with 49 additions and 73 deletions

View file

@ -1,4 +1,4 @@
import itertools
from itertools import chain, combinations
from operator import attrgetter, itemgetter
import numpy as np
@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from scipy.sparse.csgraph._shortest_path import dijkstra
from shapely import prepared
from shapely.affinity import scale
from shapely.geometry import JOIN_STYLE, LineString
from shapely.geometry import JOIN_STYLE, LineString, MultiPolygon
from shapely.ops import unary_union
from c3nav.mapdata.cache import changed_geometries
@ -19,7 +19,7 @@ from c3nav.mapdata.models import Level
from c3nav.mapdata.models.access import AccessRestrictionMixin
from c3nav.mapdata.models.geometry.base import GeometryMixin
from c3nav.mapdata.models.locations import SpecificLocation
from c3nav.mapdata.utils.geometry import (assert_multilinestring, assert_multipolygon, clean_geometry,
from c3nav.mapdata.utils.geometry import (assert_multilinestring, assert_multipolygon, clean_cut_polygon,
cut_polygon_with_line)
@ -155,87 +155,47 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
# collect all accessible areas on this level
buildings_geom = unary_union(tuple(building.geometry for building in level.buildings.all()))
for space in level.spaces.all():
spaces[space.pk] = space
space.orig_geometry = space.geometry
if space.outside:
space.geometry = space.geometry.difference(buildings_geom)
spaces[space.pk] = space
area = space.geometry
buffered = space.geometry.buffer(0.0001)
remove = unary_union(tuple(c.geometry for c in space.columns.all()) +
tuple(o.geometry for o in space.obstacles.all()) +
tuple(o.buffered_geometry for o in space.lineobstacles.all()) +
tuple(h.geometry for h in space.holes.all()))
areas.extend(assert_multipolygon(space.geometry.difference(remove)))
areas.append(space.geometry.difference(
unary_union(tuple(c.geometry for c in space.columns.all()) +
tuple(o.geometry for o in space.obstacles.all()) +
tuple(o.buffered_geometry for o in space.lineobstacles.all()) +
tuple(h.geometry for h in space.holes.all()))
))
areas = unary_union(areas+list(door.geometry for door in level.doors.all()))
# collect all stairs on this level
for space in level.spaces.all():
for stair in space.stairs.all():
substairs = tuple(assert_multilinestring(stair.geometry.intersection(buffered).difference(remove)))
for substair in substairs:
substair.space = space.pk
stairs.extend(substairs)
stairs.extend(assert_multilinestring(
stair.geometry.intersection(space.geometry.buffer(0.001, join_style=JOIN_STYLE.mitre))
))
areas = assert_multipolygon(unary_union(areas+list(door.geometry for door in level.doors.all())))
areas = [AltitudeArea(geometry=area, level=level) for area in areas]
# divide areas using stairs
for stair in stairs:
areas = MultiPolygon(cut_polygon_with_line(areas, stair))
space_areas.update({space.pk: [] for space in level.spaces.all()})
# create altitudearea objects
areas = [AltitudeArea(geometry=clean_cut_polygon(area), level=level)
for area in assert_multipolygon(areas)]
# prepare area geometries
for area in areas:
area.geometry_prep = prepared.prep(area.geometry)
# assign spaces to areas
space_areas.update({space.pk: [] for space in level.spaces.all()})
for area in areas:
area.spaces = set()
area.connected_to = []
area.geometry_prep = prepared.prep(area.geometry)
for space in level.spaces.all():
if area.geometry_prep.intersects(space.geometry):
area.spaces.add(space.pk)
space_areas[space.pk].append(area)
# divide areas using stairs
for stair in stairs:
for area in space_areas[stair.space]:
if not area.geometry_prep.intersects(stair):
continue
divided = cut_polygon_with_line(area.geometry, stair)
if len(divided) > 2:
raise ValueError
area.geometry = divided[0]
area.geometry_prep = prepared.prep(divided[0])
if len(divided) == 2:
new_area = AltitudeArea(geometry=divided[1], level=level)
new_area.geometry_prep = prepared.prep(divided[1])
new_area.spaces = set()
new_area.connected_to = [area]
area.connected_to.append(new_area)
areas.append(new_area)
original_spaces = area.spaces
if len(area.spaces) == 1:
new_area.spaces = area.spaces
space_areas[next(iter(area.spaces))].append(new_area)
else:
# update area spaces
for subarea in (area, new_area):
spaces_before = subarea.spaces
subarea.spaces = set(space for space in original_spaces
if subarea.geometry_prep.intersects(spaces[space].geometry))
for space in spaces_before-subarea.spaces:
space_areas[space].remove(subarea)
for space in subarea.spaces-spaces_before:
space_areas[space].append(subarea)
# update area connections
remove_area_connected_to = []
for other_area in area.connected_to:
if not other_area.geometry_prep.intersects(area.geometry):
new_area.connected_to.append(other_area)
remove_area_connected_to.append(other_area)
other_area.connected_to.remove(area)
other_area.connected_to.append(new_area)
elif other_area != new_area and other_area.geometry_prep.intersects(new_area.geometry):
new_area.connected_to.append(other_area)
other_area.connected_to.append(new_area)
for other_area in remove_area_connected_to:
area.connected_to.remove(other_area)
break
# give altitudes to areas
for space in level.spaces.all():
for altitudemarker in space.altitudemarkers.all():
@ -246,12 +206,23 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
else:
raise ValueError(space.title)
# determine altitude area connections
for area in areas:
area.connected_to = []
for area, other_area in combinations(areas, 2):
if area.geometry_prep.intersects(other_area.geometry):
area.connected_to.append(other_area)
other_area.connected_to.append(area)
# add areas to global areas
all_areas.extend(areas)
# for area in all_areas:
# area.geometry = clean_geometry(area.geometry)
# areas = [area for area in all_areas if not area.geometry.is_empty]
# give temporary ids to all areas
for area in all_areas:
area.geometry = clean_geometry(area.geometry)
areas = [area for area in all_areas if not area.geometry.is_empty]
areas = all_areas
for i, area in enumerate(areas):
area.tmpid = i
for area in areas:
@ -405,7 +376,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
for altitude, alt_areas in areas_by_altitude.items()}
accessible_area = accessible_area.difference(
unary_union(tuple(itertools.chain(*areas_by_altitude.values())))
unary_union(tuple(chain(*areas_by_altitude.values())))
)
stairs = []
@ -451,7 +422,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
level_areas[level] = [AltitudeArea(level=level, geometry=geometry, altitude=altitude)
for altitude, geometry in areas_by_altitude.items()]
areas = tuple(itertools.chain(*(a for a in level_areas.values())))
areas = tuple(chain(*(a for a in level_areas.values())))
for i, area in enumerate(areas):
area.tmpid = i
for level in levels:

View file

@ -133,6 +133,7 @@ def cut_line_with_point(line: LineString, point: Point):
def cut_polygon_with_line(polygon: Polygon, line: LineString):
orig_polygon = polygon
polygons = (orient(polygon) for polygon in assert_multipolygon(polygon))
polygons: List[List[LinearRing]] = [[polygon.exterior, *polygon.interiors] for polygon in polygons]
@ -153,6 +154,9 @@ def cut_polygon_with_line(polygon: Polygon, line: LineString):
# sort the points by distance along the line
points = deque(sorted(points, key=lambda p: line.project(p.point)))
if not points:
return orig_polygon
# go through all points and cut pair-wise
last = points.popleft()
while points:
@ -262,6 +266,7 @@ def cut_polygon_with_line(polygon: Polygon, line: LineString):
def clean_cut_polygon(polygon: Polygon) -> Polygon:
polygon = orient(polygon)
interiors = []
interiors.extend(cut_ring(polygon.exterior))
exteriors = [(i, ring) for (i, ring) in enumerate(interiors) if ring.is_ccw]