diff --git a/src/c3nav/editor/static/editor/js/editor.js b/src/c3nav/editor/static/editor/js/editor.js index 808b0299..be272732 100644 --- a/src/c3nav/editor/static/editor/js/editor.js +++ b/src/c3nav/editor/static/editor/js/editor.js @@ -282,7 +282,7 @@ editor = { 'levelconnector': '#FFFF00', 'shadow': '#000000', 'stair': '#FF0000', - 'areaofinterest': '#0099FF' + 'arealocation': '#0099FF' }, _line_draw_geometry_style: function(style) { style.stroke = true; @@ -304,10 +304,10 @@ editor = { var result = { stroke: false, fillColor: editor._geometry_colors[mapitem_type], - fillOpacity: (mapitem_type == 'areaofinterest') ? 0.2 : 0.6, + fillOpacity: (mapitem_type == 'arealocation') ? 0.2 : 0.6, smoothFactor: 0 }; - if (mapitem_type == 'areaofinterest') { + if (mapitem_type == 'arealocation') { result.fillOpacity = 0.02; result.color = result.fillColor; result.stroke = true; diff --git a/src/c3nav/routing/area.py b/src/c3nav/routing/area.py index c74a0693..00c73302 100644 --- a/src/c3nav/routing/area.py +++ b/src/c3nav/routing/area.py @@ -37,12 +37,20 @@ class GraphArea(): # stair checker angle = coord_angle(point1.xy, point2.xy) valid = True + direction_up = None for stair_path, stair_angle in self.mpl_stairs: if not path.intersects_path(stair_path): continue angle_diff = ((stair_angle - angle + 180) % 360) - 180 - up = angle_diff < 0 # noqa + + new_direction_up = (angle_diff > 0) + if direction_up is None: + direction_up = new_direction_up + elif direction_up != new_direction_up: + valid = False + break + if not (40 < abs(angle_diff) < 150): valid = False break @@ -50,8 +58,8 @@ class GraphArea(): if not valid: continue - point1.connect_to(point2) - point2.connect_to(point1) + 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, '')) def add_point(self, point): if not self.mpl_clear.contains_point(point.xy): diff --git a/src/c3nav/routing/connection.py b/src/c3nav/routing/connection.py index 8528978c..fe8c20a7 100644 --- a/src/c3nav/routing/connection.py +++ b/src/c3nav/routing/connection.py @@ -2,10 +2,12 @@ import numpy as np class GraphConnection(): - def __init__(self, from_point, to_point, distance=None): + def __init__(self, from_point, to_point, distance=None, ctype=''): self.from_point = from_point self.to_point = to_point self.distance = distance if distance is not None else abs(np.linalg.norm(from_point.xy - to_point.xy)) + self.ctype = ctype def __repr__(self): - return '' % (self.from_point, self.to_point, self.distance) + return ('' % + (self.from_point, self.to_point, self.distance, self.ctype)) diff --git a/src/c3nav/routing/graph.py b/src/c3nav/routing/graph.py index a7494fba..91d23fb5 100644 --- a/src/c3nav/routing/graph.py +++ b/src/c3nav/routing/graph.py @@ -174,7 +174,7 @@ class Graph: level.draw_png(points, lines) # Router - def build_routers(self): + def build_routers(self, allowed_ctypes): routers = {} empty_distances = np.empty(shape=(len(self.level_transfer_points),) * 2, dtype=np.float16) @@ -186,7 +186,7 @@ class Graph: level_transfers[:] = -1 for i, level in enumerate(self.levels.values()): - routers.update(level.build_routers()) + routers.update(level.build_routers(allowed_ctypes)) router = routers[level] level_distances = empty_distances.copy() @@ -220,7 +220,7 @@ class Graph: def _allowed_points_index(self, points, allowed_points_i): return np.array(tuple(i for i, point in enumerate(points) if point in allowed_points_i)) - def get_route(self, origin: Location, destination: Location): + def get_route(self, origin: Location, destination: Location, allowed_ctypes): orig_points_i = set(self.get_location_points(origin)) dest_points_i = set(self.get_location_points(destination)) @@ -230,7 +230,7 @@ class Graph: best_route = NoRoute # get routers - routers = self.build_routers() + routers = self.build_routers(allowed_ctypes) # route within room orig_rooms = set(point.room for point in orig_points) @@ -273,6 +273,7 @@ class Graph: for level in orig_levels} dest_room_transfer_points = {level: self._allowed_points_index(level.room_transfer_points, dest_room_transfers) for level in dest_levels} + print(dest_room_transfer_points) # if the points have common rooms, search for routes within thos levels if common_levels: @@ -305,10 +306,12 @@ class Graph: # as a dictionary: global transfer point index => Route orig_level_transfers = self._level_transfers(orig_levels, orig_room_transfers, routers, mode='orig') dest_level_transfers = self._level_transfers(orig_levels, dest_room_transfers, routers, mode='dest') + print(orig_levels, dest_room_transfers, dest_level_transfers) - # get reachable roomtransfer points for each level (points as room transfer point index within level) + # get reachable leveltransfer points (points as level transfer point index within graph) orig_level_transfer_points = self._allowed_points_index(self.level_transfer_points, orig_level_transfers) dest_level_transfer_points = self._allowed_points_index(self.level_transfer_points, dest_level_transfers) + print(dest_level_transfer_points) # search a route within the whole graph if True: diff --git a/src/c3nav/routing/level.py b/src/c3nav/routing/level.py index 911267bf..90397b9a 100644 --- a/src/c3nav/routing/level.py +++ b/src/c3nav/routing/level.py @@ -156,6 +156,12 @@ class GraphLevel(): if mpl_arealocation.contains_point(point.xy)) # Drawing + ctype_colors = { + '': (50, 200, 0), + 'steps_up': (255, 50, 50), + 'steps_down': (255, 50, 50), + } + def draw_png(self, points=True, lines=True): filename = os.path.join(settings.RENDER_ROOT, 'base-level-%s.png' % self.level.name) graph_filename = os.path.join(settings.RENDER_ROOT, 'graph-level-%s.png' % self.level.name) @@ -163,12 +169,17 @@ class GraphLevel(): im = Image.open(filename) height = im.size[1] draw = ImageDraw.Draw(im) + if lines: for room in self.rooms: # noinspection PyTypeChecker - for from_i, to_i in np.argwhere(room.distances != np.inf): + for ctype, from_i, to_i in np.argwhere(room.distances != np.inf): draw.line(_line_coords(self.graph.points[room.points[from_i]], - self.graph.points[room.points[to_i]], height), fill=(255, 100, 100)) + self.graph.points[room.points[to_i]], height), + fill=self.ctype_colors[room.ctypes[ctype]]) + if room.ctypes[ctype] == 'steps_up': + point = self.graph.points[room.points[from_i]] + draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 255, 255)) if points: for point_i in self.points: @@ -186,7 +197,7 @@ class GraphLevel(): if lines: for room in self.rooms: # noinspection PyTypeChecker - for from_i, to_i in np.argwhere(room.distances != np.inf): + for ctype, from_i, to_i in np.argwhere(room.distances != np.inf): if room.points[from_i] in room.room_transfer_points: draw.line(_line_coords(self.graph.points[room.points[from_i]], self.graph.points[room.points[to_i]], height), fill=(0, 255, 255)) @@ -194,7 +205,7 @@ class GraphLevel(): im.save(graph_filename) # Routing - def build_routers(self): + def build_routers(self, allowed_ctypes): routers = {} empty_distances = np.empty(shape=(len(self.room_transfer_points),) * 2, dtype=np.float16) @@ -206,7 +217,7 @@ class GraphLevel(): room_transfers[:] = -1 for i, room in enumerate(self.rooms): - router = room.build_router() + router = room.build_router(allowed_ctypes) routers[room] = router room_distances = empty_distances.copy() diff --git a/src/c3nav/routing/point.py b/src/c3nav/routing/point.py index d42ee3e2..b0d160e3 100644 --- a/src/c3nav/routing/point.py +++ b/src/c3nav/routing/point.py @@ -11,6 +11,7 @@ class GraphPoint(): self.y = y self.room = room self.xy = np.array((x, y)) + self.i = None self.connections = {} self.connections_in = {} @@ -32,8 +33,8 @@ class GraphPoint(): y = self.y * settings.RENDER_SCALE return ((x-5, y-5), (x+5, y+5)) - def connect_to(self, other_point): - connection = GraphConnection(self, other_point) + def connect_to(self, other_point, ctype=''): + connection = GraphConnection(self, other_point, ctype=ctype) self.connections[other_point] = connection other_point.connections_in[self] = connection diff --git a/src/c3nav/routing/room.py b/src/c3nav/routing/room.py index 9de990d0..5ff2c345 100644 --- a/src/c3nav/routing/room.py +++ b/src/c3nav/routing/room.py @@ -22,10 +22,12 @@ class GraphRoom(): self.mpl_clear = None + self.i = None self.areas = [] self.points = None self.room_transfer_points = None self.distances = np.zeros((1, )) + self.ctypes = None def serialize(self): return ( @@ -34,12 +36,13 @@ class GraphRoom(): self.points, self.room_transfer_points, self.distances, + self.ctypes, ) @classmethod def unserialize(cls, level, data): room = cls(level) - room.mpl_clear, areas, room.points, room.room_transfer_points, room.distances = data + room.mpl_clear, areas, room.points, room.room_transfer_points, room.distances, room.ctypes = data room.areas = tuple(GraphArea(room, *area) for area in areas) return room @@ -183,6 +186,8 @@ class GraphRoom(): return [point] def build_connections(self): + print('\n\n') + print('room') for area in self.areas: area.build_connections() @@ -195,32 +200,49 @@ class GraphRoom(): self.room_transfer_points = tuple(i for i in self.points if i in self.level.room_transfer_points) mapping = {point.i: i for i, point in enumerate(self._built_points)} - self.distances = np.empty(shape=(len(self._built_points), len(self._built_points)), dtype=np.float16) - self.distances[:] = np.inf + + empty = np.empty(shape=(len(self._built_points), len(self._built_points)), dtype=np.float16) + empty[:] = np.inf + + ctypes = [] + distances = {} for from_point in self._built_points: for to_point, connection in from_point.connections.items(): if to_point.i in mapping: - self.distances[mapping[from_point.i], mapping[to_point.i]] = connection.distance + if connection.ctype not in distances: + ctypes.append(connection.ctype) + distances[connection.ctype] = empty.copy() + distances[connection.ctype][mapping[from_point.i], mapping[to_point.i]] = connection.distance + + self.ctypes = tuple(ctypes) + self.distances = np.array(tuple(distances[ctype] for ctype in ctypes)) for area in self.areas: area.finish_build() # Routing - def build_router(self): - cache_key = 'c3nav__graph__roomrouter__%s__%s' % (self.graph.mtime, self.i) + def build_router(self, allowed_ctypes): + ctypes = tuple(i for i, ctype in enumerate(self.ctypes) if ctype in allowed_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) if not roomrouter: - roomrouter = self._build_router() + roomrouter = self._build_router(ctypes) cache.set(cache_key, roomrouter, 600) return roomrouter - def _build_router(self): - g_sparse = csgraph_from_dense(self.distances, null_value=np.inf) + def _build_router(self, ctypes): + g_sparse = csgraph_from_dense(np.amin(self.distances[ctypes, :, :], 0), null_value=np.inf) shortest_paths, predecessors = shortest_path(g_sparse, return_predecessors=True) return RoomRouter(shortest_paths, predecessors) def get_connection(self, from_i, to_i): - return GraphConnection(self.graph.points[self.points[from_i]], self.graph.points[self.points[to_i]]) + stack = self.distances[:, from_i, to_i] + min_i = stack.argmin() + distance = stack[min_i] + ctype = self.ctypes[min_i] + return GraphConnection(self.graph.points[self.points[from_i]], self.graph.points[self.points[to_i]], + distance=distance, ctype=ctype) RoomRouter = namedtuple('RoomRouter', ('shortest_paths', 'predecessors', )) diff --git a/src/c3nav/routing/route.py b/src/c3nav/routing/route.py index 21a7c326..c14d033f 100644 --- a/src/c3nav/routing/route.py +++ b/src/c3nav/routing/route.py @@ -41,36 +41,36 @@ class RoutePart: self.level_name = level.level.name self.connections = connections - width, height = get_dimensions() + svg_width, svg_height = get_dimensions() points = (connections[0].from_point, ) + tuple(connection.to_point for connection in connections) for point in points: point.svg_x = point.x * 6 - point.svg_y = (height - point.y) * 6 + point.svg_y = (svg_height - point.y) * 6 x, y = zip(*((point.svg_x, point.svg_y) for point in points if point.level == level)) self.distance = sum(connection.distance for connection in connections) # bounds for rendering - self.min_x = min(x) - 20 - self.max_x = max(x) + 20 - self.min_y = min(y) - 20 - self.max_y = max(y) + 20 + self.svg_min_x = min(x) - 20 + self.svg_max_x = max(x) + 20 + self.svg_min_y = min(y) - 20 + self.svg_max_y = max(y) + 20 - width = self.max_x - self.min_x - height = self.max_y - self.min_y + svg_width = self.svg_max_x - self.svg_min_x + svg_height = self.svg_max_y - self.svg_min_y - if width < 150: - self.min_x -= (10 - width) / 2 - self.max_x += (10 - width) / 2 + if svg_width < 150: + self.svg_min_x -= (10 - svg_width) / 2 + self.svg_max_x += (10 - svg_width) / 2 - if height < 150: - self.min_y -= (10 - height) / 2 - self.max_y += (10 - height) / 2 + if svg_height < 150: + self.svg_min_y += (10 - svg_height) / 2 + self.svg_max_y -= (10 - svg_height) / 2 - self.width = self.max_x - self.min_x - self.height = self.max_y - self.min_y + self.svg_width = self.svg_max_x - self.svg_min_x + self.svg_height = self.svg_max_y - self.svg_min_y def __str__(self): return repr(self.__dict__) diff --git a/src/c3nav/site/templates/site/fragment_route.html b/src/c3nav/site/templates/site/fragment_route.html new file mode 100644 index 00000000..e03a5ba8 --- /dev/null +++ b/src/c3nav/site/templates/site/fragment_route.html @@ -0,0 +1,28 @@ +{% load route_render %} + +

Your Route

+
+ {% for routepart in route.routeparts %} + + + + + + + + + + + {% for c in routepart.connections %} + + {% endfor %} + + + {% endfor %} +
diff --git a/src/c3nav/site/templates/site/main.html b/src/c3nav/site/templates/site/main.html index 2585b605..4e9ccd9f 100644 --- a/src/c3nav/site/templates/site/main.html +++ b/src/c3nav/site/templates/site/main.html @@ -2,7 +2,6 @@ {% load static %} {% load i18n %} -{% load route_render %} {% block content %}
@@ -15,32 +14,7 @@
- {% if route %} -

Your Route

-
- {% for routepart in route.routeparts %} - - - - - - - - - - - {% for c in routepart.connections %} - - {% endfor %} - - - {% endfor %} -
- {% endif %} +{% if route %} +{% include 'site/fragment_route.html' %} +{% endif %} {% endblock %} diff --git a/src/c3nav/site/views.py b/src/c3nav/site/views.py index c6cc2142..2c727ed4 100644 --- a/src/c3nav/site/views.py +++ b/src/c3nav/site/views.py @@ -42,7 +42,7 @@ def main(request, origin=None, destination=None): route = None if origin and destination: graph = Graph.load() - route = graph.get_route(origin, destination) + route = graph.get_route(origin, destination, ('', 'steps_down', 'steps_up')) route = route.split() print(route)