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):
|
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 = []
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue