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):
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
def stair_areas(self):
left = []

View file

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

View file

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

View file

@ -3,15 +3,17 @@ from collections import namedtuple
import numpy as np
from django.conf import settings
from matplotlib.path import Path
from PIL import Image, ImageDraw
from scipy.sparse.csgraph._shortest_path import shortest_path
from scipy.sparse.csgraph._tools import csgraph_from_dense
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.room import GraphRoom
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.mpl import shapely_to_mpl
@ -48,6 +50,9 @@ class GraphLevel():
self._built_points = []
self._built_room_transfer_points = []
self.collect_stairs()
self.collect_escalators()
self.collect_rooms()
print('%d rooms' % len(self.rooms))
@ -74,6 +79,30 @@ class GraphLevel():
def connection_count(self):
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):
accessibles = self.level.geometries.accessible
accessibles = assert_multipolygon(accessibles)
@ -183,6 +212,8 @@ class GraphLevel():
'': (50, 200, 0),
'steps_up': (255, 50, 50),
'steps_down': (255, 50, 50),
'escalator_up': (255, 150, 0),
'escalator_down': (200, 100, 0),
'elevator_up': (200, 0, 200),
'elevator_down': (200, 0, 200),
}
@ -260,3 +291,4 @@ class GraphLevel():
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._tools import csgraph_from_dense
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.routing.area import GraphArea
from c3nav.routing.connection import GraphConnection
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
@ -58,10 +59,10 @@ class GraphRoom():
self._built_is_elevatorlevel = False
self.mpl_clear = shapely_to_mpl(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre))
self.mpl_stairs = ()
for stair_line in assert_multilinestring(self.level.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:]))
self.mpl_stairs = tuple((stair, angle) for stair, angle in self.level.mpl_stairs
if self.mpl_clear.intersects_path(stair, filled=True))
self._built_escalators = tuple(escalator for escalator in self.level._built_escalators
if self.mpl_clear.intersects_path(escalator.mpl_geom.exterior, filled=True))
self.isolated_areas = []
return True
@ -70,16 +71,25 @@ class GraphRoom():
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.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(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:
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
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()
self.areas.append(area)
@ -120,7 +130,7 @@ class GraphRoom():
points += self._add_ring(interior, want_left=True)
# 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 linestring in assert_multilinestring(ring.intersection(self.clear_geometry)):
coords = tuple(linestring.coords)
@ -225,7 +235,6 @@ class GraphRoom():
# Routing
def build_router(self, 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' %
(self.graph.mtime, self.i, ','.join(str(i) for i in ctypes)))
roomrouter = cache.get(cache_key)