try to call heavy shapely operations less often to improve performance

This commit is contained in:
Laura Klünder 2017-11-18 22:44:06 +01:00
parent b6be667cfc
commit b700ccd46c

View file

@ -159,10 +159,20 @@ class AltitudeAreaGeometries:
crops=crops) crops=crops)
class FakeCropper: empty_geometry_collection = GeometryCollection()
@staticmethod
def intersection(other):
return other class Cropper:
def __init__(self, geometry=None):
self.geometry = geometry
self.geometry_prep = None if geometry is None else prepared.prep(geometry)
def intersection(self, other):
if self.geometry is None:
return other
if self.geometry_prep.intersects(other):
return self.geometry.intersection(other)
return empty_geometry_collection
class LevelRenderData: class LevelRenderData:
@ -224,7 +234,7 @@ class LevelRenderData:
primary_level_count += 1 primary_level_count += 1
# set crop area if we area on the second primary layer from top or below # set crop area if we area on the second primary layer from top or below
level_crop_to[sublevel.pk] = crop_to if primary_level_count > 1 else FakeCropper level_crop_to[sublevel.pk] = Cropper(crop_to if primary_level_count > 1 else None)
if geoms.holes is not None: if geoms.holes is not None:
if crop_to is None: if crop_to is None:
@ -246,8 +256,8 @@ class LevelRenderData:
old_geoms = single_level_geoms[sublevel.pk] old_geoms = single_level_geoms[sublevel.pk]
if crop_to is not FakeCropper: if crop_to.geometry is not None:
map_history.composite(MapHistory.open_level(sublevel.pk, 'base'), crop_to) map_history.composite(MapHistory.open_level(sublevel.pk, 'base'), crop_to.geometry)
elif level.pk != sublevel.pk: elif level.pk != sublevel.pk:
map_history.composite(MapHistory.open_level(sublevel.pk, 'base'), None) map_history.composite(MapHistory.open_level(sublevel.pk, 'base'), None)
@ -264,6 +274,7 @@ class LevelRenderData:
new_geometry = crop_to.intersection(altitudearea.geometry) new_geometry = crop_to.intersection(altitudearea.geometry)
if new_geometry.is_empty: if new_geometry.is_empty:
continue continue
new_geometry_prep = prepared.prep(new_geometry)
new_altitudearea = AltitudeAreaGeometries() new_altitudearea = AltitudeAreaGeometries()
new_altitudearea.geometry = new_geometry new_altitudearea.geometry = new_geometry
@ -276,6 +287,8 @@ class LevelRenderData:
for color, areas in altitudearea.colors.items(): for color, areas in altitudearea.colors.items():
new_areas = {} new_areas = {}
for access_restriction, area in areas.items(): for access_restriction, area in areas.items():
if not new_geometry_prep.intersects(area):
continue
new_area = new_geometry.intersection(area) new_area = new_geometry.intersection(area)
if not new_area.is_empty: if not new_area.is_empty:
new_areas[access_restriction] = new_area new_areas[access_restriction] = new_area
@ -285,7 +298,7 @@ class LevelRenderData:
new_altitudearea.obstacles = {key: new_geometry.intersection(areas) new_altitudearea.obstacles = {key: new_geometry.intersection(areas)
for key, areas in altitudearea.obstacles.items() for key, areas in altitudearea.obstacles.items()
if new_geometry.intersects(areas)} if new_geometry_prep.intersects(areas)}
new_geoms.altitudeareas.append(new_altitudearea) new_geoms.altitudeareas.append(new_altitudearea)
@ -436,18 +449,27 @@ class LevelGeometries:
self.door_height = None self.door_height = None
self.min_altitude = None self.min_altitude = None
@staticmethod @classmethod
def build_for_level(level, altitudeareas_above): def build_for_level(cls, level, altitudeareas_above):
geoms = LevelGeometries() geoms = LevelGeometries()
buildings_geom = unary_union([b.geometry for b in level.buildings.all()]) buildings_geom = unary_union([b.geometry for b in level.buildings.all()])
buildings_geom_prep = prepared.prep(buildings_geom)
# remove columns and holes from space areas # remove columns and holes from space areas
for space in level.spaces.all(): for space in level.spaces.all():
subtract = []
if space.outside: if space.outside:
space.geometry = space.geometry.difference(buildings_geom) subtract.append(buildings_geom)
space.geometry = space.geometry.difference(unary_union([c.geometry for c in space.columns.all()])) if subtract:
space.holes_geom = unary_union([h.geometry for h in space.holes.all()]) space.geometry = space.geometry.difference(unary_union(subtract))
space.walkable_geom = space.geometry.difference(space.holes_geom)
holes = tuple(h.geometry for h in space.holes.all())
if holes:
space.holes_geom = unary_union([h.geometry for h in space.holes.all()])
space.walkable_geom = space.geometry.difference(space.holes_geom)
else:
space.holes_geom = empty_geometry_collection
space.walkable_geom = space.geometry
spaces_geom = unary_union([s.geometry for s in level.spaces.all()]) spaces_geom = unary_union([s.geometry for s in level.spaces.all()])
doors_geom = unary_union([d.geometry for d in level.doors.all()]) doors_geom = unary_union([d.geometry for d in level.doors.all()])
@ -475,11 +497,13 @@ class LevelGeometries:
buffered = space.geometry.buffer(0.01).union(unary_union( buffered = space.geometry.buffer(0.01).union(unary_union(
tuple(door.geometry for door in level.doors.all() if door.geometry.intersects(space.geometry)) tuple(door.geometry for door in level.doors.all() if door.geometry.intersects(space.geometry))
).difference(walkable_spaces_geom)) ).difference(walkable_spaces_geom))
if buffered.intersects(buildings_geom):
intersects = buildings_geom_prep.intersects(buffered)
if intersects:
restricted_spaces_indoors.setdefault(access_restriction, []).append( restricted_spaces_indoors.setdefault(access_restriction, []).append(
buffered.intersection(buildings_geom) buffered.intersection(buildings_geom)
) )
if not buffered.within(buildings_geom): if not intersects or not buildings_geom_prep.contains(buffered):
restricted_spaces_outdoors.setdefault(access_restriction, []).append( restricted_spaces_outdoors.setdefault(access_restriction, []).append(
buffered.difference(buildings_geom) buffered.difference(buildings_geom)
) )
@ -517,15 +541,16 @@ class LevelGeometries:
# add altitudegroup geometries and split ground colors into them # add altitudegroup geometries and split ground colors into them
for altitudearea in level.altitudeareas.all(): for altitudearea in level.altitudeareas.all():
altitudearea_prep = prepared.prep(altitudearea.geometry)
altitudearea_colors = {color: {access_restriction: area.intersection(altitudearea.geometry) altitudearea_colors = {color: {access_restriction: area.intersection(altitudearea.geometry)
for access_restriction, area in areas.items() for access_restriction, area in areas.items()
if area.intersects(altitudearea.geometry)} if altitudearea_prep.intersects(area)}
for color, areas in colors.items()} for color, areas in colors.items()}
altitudearea_colors = {color: areas for color, areas in altitudearea_colors.items() if areas} altitudearea_colors = {color: areas for color, areas in altitudearea_colors.items() if areas}
altitudearea_obstacles = {height: area.intersection(altitudearea.geometry) altitudearea_obstacles = {height: area.intersection(altitudearea.geometry)
for height, area in obstacles.items() for height, area in obstacles.items()
if area.intersects(altitudearea.geometry)} if altitudearea_prep.intersects(area)}
geoms.altitudeareas.append(AltitudeAreaGeometries(altitudearea, geoms.altitudeareas.append(AltitudeAreaGeometries(altitudearea,
altitudearea_colors, altitudearea_colors,
altitudearea_obstacles)) altitudearea_obstacles))
@ -542,7 +567,7 @@ class LevelGeometries:
geoms.restricted_spaces_outdoors = {access_restriction: unary_union(spaces) geoms.restricted_spaces_outdoors = {access_restriction: unary_union(spaces)
for access_restriction, spaces in restricted_spaces_outdoors.items()} for access_restriction, spaces in restricted_spaces_outdoors.items()}
geoms.walls = buildings_geom.difference(spaces_geom).difference(doors_geom) geoms.walls = buildings_geom.difference(unary_union((spaces_geom, doors_geom)))
# shorten walls if there are altitudeareas above # shorten walls if there are altitudeareas above
remaining = geoms.walls remaining = geoms.walls