buildgraph: add support for escalator_up and escalator_down

This commit is contained in:
Laura Klünder 2016-12-18 13:28:02 +01:00
parent d2e9e18c2a
commit 405c688eac
5 changed files with 100 additions and 18 deletions

View file

@ -237,6 +237,10 @@ class LevelGeometries():
def stairs(self): def stairs(self):
return cascaded_union([stair.geometry for stair in self.query('stairs')]).intersection(self.accessible) return cascaded_union([stair.geometry for stair in self.query('stairs')]).intersection(self.accessible)
@cached_property
def escalatorslopes(self):
return cascaded_union([s.geometry for s in self.query('escalatorslopes')]).intersection(self.accessible)
@cached_property @cached_property
def stair_areas(self): def stair_areas(self):
left = [] left = []

View file

@ -25,6 +25,8 @@ def assert_multipolygon(geometry):
:param geometry: a Polygon or a MultiPolygon :param geometry: a Polygon or a MultiPolygon
:return: a list of Polygons :return: a list of Polygons
""" """
if geometry.is_empty:
return []
if isinstance(geometry, Polygon): if isinstance(geometry, Polygon):
return [geometry] return [geometry]
return geometry.geoms return geometry.geoms
@ -36,6 +38,8 @@ def assert_multilinestring(geometry):
:param geometry: a Geometry or a GeometryCollection :param geometry: a Geometry or a GeometryCollection
:return: a list of Geometries :return: a list of Geometries
""" """
if geometry.is_empty:
return []
if isinstance(geometry, LineString): if isinstance(geometry, LineString):
return [geometry] return [geometry]
return geometry.geoms return geometry.geoms

View file

@ -7,12 +7,13 @@ from c3nav.routing.utils.coords import coord_angle
class GraphArea(): class GraphArea():
def __init__(self, room, mpl_clear, mpl_stairs, points=None): def __init__(self, room, mpl_clear, mpl_stairs, escalators, points=None):
self.room = room self.room = room
self.graph = room.graph self.graph = room.graph
self.mpl_clear = mpl_clear self.mpl_clear = mpl_clear
self.mpl_stairs = mpl_stairs self.mpl_stairs = mpl_stairs
self.escalators = escalators
self.points = points self.points = points
@ -20,6 +21,7 @@ class GraphArea():
return ( return (
self.mpl_clear, self.mpl_clear,
self.mpl_stairs, self.mpl_stairs,
self.escalators,
self.points, self.points,
) )
@ -37,7 +39,7 @@ class GraphArea():
# stair checker # stair checker
angle = coord_angle(point1.xy, point2.xy) angle = coord_angle(point1.xy, point2.xy)
valid = True valid = True
direction_up = None stair_direction_up = None
for stair_path, stair_angle in self.mpl_stairs: for stair_path, stair_angle in self.mpl_stairs:
if not path.intersects_path(stair_path): if not path.intersects_path(stair_path):
continue continue
@ -45,9 +47,9 @@ class GraphArea():
angle_diff = ((stair_angle - angle + 180) % 360) - 180 angle_diff = ((stair_angle - angle + 180) % 360) - 180
new_direction_up = (angle_diff > 0) new_direction_up = (angle_diff > 0)
if direction_up is None: if stair_direction_up is None:
direction_up = new_direction_up stair_direction_up = new_direction_up
elif direction_up != new_direction_up: elif stair_direction_up != new_direction_up:
valid = False valid = False
break break
@ -55,11 +57,42 @@ class GraphArea():
valid = False valid = False
break break
if not valid:
break
# escalator checker
angle = coord_angle(point1.xy, point2.xy)
valid = True
escalator_direction_up = None
escalator_swap_direction = False
for escalator in self.escalators:
if not escalator.mpl_geom.intersects_path(path, filled=True):
continue
if escalator_direction_up is not None:
# only one escalator per connection
valid = False
break
angle_diff = ((escalator.angle - angle + 180) % 360) - 180
escalator_direction_up = (angle_diff > 0)
escalator_swap_direction = (escalator_direction_up != escalator.direction_up)
if not valid: if not valid:
continue continue
point1.connect_to(point2, ctype={True: 'steps_up', False: 'steps_down'}.get(direction_up, '')) if stair_direction_up is not None:
point2.connect_to(point1, ctype={True: 'steps_down', False: 'steps_up'}.get(direction_up, '')) point1.connect_to(point2, ctype=('steps_up' if stair_direction_up else 'steps_down'))
point2.connect_to(point1, ctype=('steps_down' if stair_direction_up else 'steps_up'))
elif escalator_direction_up is not None:
if not escalator_swap_direction:
point1.connect_to(point2, ctype=('escalator_up' if escalator_direction_up else 'escalator_down'))
else:
point2.connect_to(point1, ctype=('escalator_down' if escalator_direction_up else 'escalator_up'))
else:
point1.connect_to(point2)
point2.connect_to(point1)
def add_point(self, point): def add_point(self, point):
if not self.mpl_clear.contains_point(point.xy): if not self.mpl_clear.contains_point(point.xy):

View file

@ -3,15 +3,17 @@ from collections import namedtuple
import numpy as np import numpy as np
from django.conf import settings from django.conf import settings
from matplotlib.path import Path
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from scipy.sparse.csgraph._shortest_path import shortest_path from scipy.sparse.csgraph._shortest_path import shortest_path
from scipy.sparse.csgraph._tools import csgraph_from_dense from scipy.sparse.csgraph._tools import csgraph_from_dense
from shapely.geometry import JOIN_STYLE from shapely.geometry import JOIN_STYLE
from c3nav.mapdata.utils.geometry import assert_multipolygon from c3nav.mapdata.utils.geometry import assert_multilinestring, assert_multipolygon
from c3nav.routing.point import GraphPoint from c3nav.routing.point import GraphPoint
from c3nav.routing.room import GraphRoom from c3nav.routing.room import GraphRoom
from c3nav.routing.utils.base import get_nearest_point from c3nav.routing.utils.base import get_nearest_point
from c3nav.routing.utils.coords import coord_angle
from c3nav.routing.utils.draw import _ellipse_bbox, _line_coords from c3nav.routing.utils.draw import _ellipse_bbox, _line_coords
from c3nav.routing.utils.mpl import shapely_to_mpl from c3nav.routing.utils.mpl import shapely_to_mpl
@ -48,6 +50,9 @@ class GraphLevel():
self._built_points = [] self._built_points = []
self._built_room_transfer_points = [] self._built_room_transfer_points = []
self.collect_stairs()
self.collect_escalators()
self.collect_rooms() self.collect_rooms()
print('%d rooms' % len(self.rooms)) print('%d rooms' % len(self.rooms))
@ -74,6 +79,30 @@ class GraphLevel():
def connection_count(self): def connection_count(self):
return sum(room.connection_count() for room in self.rooms) return sum(room.connection_count() for room in self.rooms)
def collect_stairs(self):
self.mpl_stairs = ()
for stair_line in assert_multilinestring(self.level.geometries.stairs):
coords = tuple(stair_line.coords)
self.mpl_stairs += tuple((Path(part), coord_angle(*part)) for part in zip(coords[:-1], coords[1:]))
def collect_escalators(self):
self.mpl_escalatorslopes = ()
for escalatorslope_line in assert_multilinestring(self.level.geometries.escalatorslopes):
coords = tuple(escalatorslope_line.coords)
self.mpl_escalatorslopes += tuple((Path(part), coord_angle(*part))
for part in zip(coords[:-1], coords[1:]))
self._built_escalators = []
for escalator in self.level.escalators.all():
mpl_escalator = shapely_to_mpl(escalator.geometry)
for slope_line, angle in self.mpl_escalatorslopes:
if mpl_escalator.intersects_path(slope_line, filled=True):
self._built_escalators.append(EscalatorData(mpl_escalator, escalator.direction, slope_line, angle))
break
else:
print('Escalator %s has no slope line!' % escalator.name)
continue
def collect_rooms(self): def collect_rooms(self):
accessibles = self.level.geometries.accessible accessibles = self.level.geometries.accessible
accessibles = assert_multipolygon(accessibles) accessibles = assert_multipolygon(accessibles)
@ -183,6 +212,8 @@ class GraphLevel():
'': (50, 200, 0), '': (50, 200, 0),
'steps_up': (255, 50, 50), 'steps_up': (255, 50, 50),
'steps_down': (255, 50, 50), 'steps_down': (255, 50, 50),
'escalator_up': (255, 150, 0),
'escalator_down': (200, 100, 0),
'elevator_up': (200, 0, 200), 'elevator_up': (200, 0, 200),
'elevator_down': (200, 0, 200), 'elevator_down': (200, 0, 200),
} }
@ -260,3 +291,4 @@ class GraphLevel():
LevelRouter = namedtuple('LevelRouter', ('shortest_paths', 'predecessors', 'room_transfers', )) LevelRouter = namedtuple('LevelRouter', ('shortest_paths', 'predecessors', 'room_transfers', ))
EscalatorData = namedtuple('EscalatorData', ('mpl_geom', 'direction_up', 'slope', 'angle'))

View file

@ -6,12 +6,13 @@ from matplotlib.path import Path
from scipy.sparse.csgraph._shortest_path import shortest_path from scipy.sparse.csgraph._shortest_path import shortest_path
from scipy.sparse.csgraph._tools import csgraph_from_dense from scipy.sparse.csgraph._tools import csgraph_from_dense
from shapely.geometry import CAP_STYLE, JOIN_STYLE, LineString from shapely.geometry import CAP_STYLE, JOIN_STYLE, LineString
from shapely.ops import cascaded_union
from c3nav.mapdata.utils.geometry import assert_multilinestring, assert_multipolygon from c3nav.mapdata.utils.geometry import assert_multilinestring, assert_multipolygon
from c3nav.routing.area import GraphArea from c3nav.routing.area import GraphArea
from c3nav.routing.connection import GraphConnection from c3nav.routing.connection import GraphConnection
from c3nav.routing.point import GraphPoint from c3nav.routing.point import GraphPoint
from c3nav.routing.utils.coords import coord_angle, get_coords_angles from c3nav.routing.utils.coords import get_coords_angles
from c3nav.routing.utils.mpl import shapely_to_mpl from c3nav.routing.utils.mpl import shapely_to_mpl
@ -58,10 +59,10 @@ class GraphRoom():
self._built_is_elevatorlevel = False self._built_is_elevatorlevel = False
self.mpl_clear = shapely_to_mpl(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre)) self.mpl_clear = shapely_to_mpl(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre))
self.mpl_stairs = () self.mpl_stairs = tuple((stair, angle) for stair, angle in self.level.mpl_stairs
for stair_line in assert_multilinestring(self.level.level.geometries.stairs): if self.mpl_clear.intersects_path(stair, filled=True))
coords = tuple(stair_line.coords) self._built_escalators = tuple(escalator for escalator in self.level._built_escalators
self.mpl_stairs += tuple((Path(part), coord_angle(*part)) for part in zip(coords[:-1], coords[1:])) if self.mpl_clear.intersects_path(escalator.mpl_geom.exterior, filled=True))
self.isolated_areas = [] self.isolated_areas = []
return True return True
@ -70,16 +71,25 @@ class GraphRoom():
stairs_areas = self.level.level.geometries.stairs stairs_areas = self.level.level.geometries.stairs
stairs_areas = stairs_areas.buffer(0.3, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) stairs_areas = stairs_areas.buffer(0.3, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat)
stairs_areas = stairs_areas.intersection(self._built_geometry) stairs_areas = stairs_areas.intersection(self._built_geometry)
self.stairs_areas = assert_multipolygon(stairs_areas) self._built_isolated_areas = tuple(assert_multipolygon(stairs_areas))
escalators_areas = self.level.level.geometries.escalators
escalators_areas = escalators_areas.intersection(self._built_geometry)
self._built_isolated_areas += tuple(assert_multipolygon(escalators_areas))
escalators_and_stairs = cascaded_union((stairs_areas, escalators_areas))
isolated_areas = tuple(assert_multipolygon(stairs_areas.intersection(self.clear_geometry))) isolated_areas = tuple(assert_multipolygon(stairs_areas.intersection(self.clear_geometry)))
isolated_areas += tuple(assert_multipolygon(self.clear_geometry.difference(stairs_areas))) isolated_areas += tuple(assert_multipolygon(escalators_areas.intersection(self.clear_geometry)))
isolated_areas += tuple(assert_multipolygon(self.clear_geometry.difference(escalators_and_stairs)))
for isolated_area in isolated_areas: for isolated_area in isolated_areas:
mpl_clear = shapely_to_mpl(isolated_area.buffer(0.01, join_style=JOIN_STYLE.mitre)) mpl_clear = shapely_to_mpl(isolated_area.buffer(0.01, join_style=JOIN_STYLE.mitre))
mpl_stairs = tuple((stair, angle) for stair, angle in self.mpl_stairs mpl_stairs = tuple((stair, angle) for stair, angle in self.mpl_stairs
if mpl_clear.intersects_path(stair, filled=True)) if mpl_clear.intersects_path(stair, filled=True))
area = GraphArea(self, mpl_clear, mpl_stairs) escalators = tuple(escalator for escalator in self._built_escalators
if escalator.mpl_geom.intersects_path(mpl_clear.exterior, filled=True))
area = GraphArea(self, mpl_clear, mpl_stairs, escalators)
area.prepare_build() area.prepare_build()
self.areas.append(area) self.areas.append(area)
@ -120,7 +130,7 @@ class GraphRoom():
points += self._add_ring(interior, want_left=True) points += self._add_ring(interior, want_left=True)
# points around steps # points around steps
for polygon in self.stairs_areas: for polygon in self._built_isolated_areas:
for ring in (polygon.exterior, )+tuple(polygon.interiors): for ring in (polygon.exterior, )+tuple(polygon.interiors):
for linestring in assert_multilinestring(ring.intersection(self.clear_geometry)): for linestring in assert_multilinestring(ring.intersection(self.clear_geometry)):
coords = tuple(linestring.coords) coords = tuple(linestring.coords)
@ -225,7 +235,6 @@ class GraphRoom():
# Routing # Routing
def build_router(self, allowed_ctypes): def build_router(self, allowed_ctypes):
ctypes = tuple(i for i, ctype in enumerate(self.ctypes) if ctype in allowed_ctypes) ctypes = tuple(i for i, ctype in enumerate(self.ctypes) if ctype in allowed_ctypes)
print(self.ctypes)
cache_key = ('c3nav__graph__roomrouter__%s__%s__%s' % cache_key = ('c3nav__graph__roomrouter__%s__%s__%s' %
(self.graph.mtime, self.i, ','.join(str(i) for i in ctypes))) (self.graph.mtime, self.i, ','.join(str(i) for i in ctypes)))
roomrouter = cache.get(cache_key) roomrouter = cache.get(cache_key)