buildgraph: add support for escalator_up and escalator_down
This commit is contained in:
parent
d2e9e18c2a
commit
405c688eac
5 changed files with 100 additions and 18 deletions
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue