add graph router

This commit is contained in:
Laura Klünder 2016-12-05 13:39:22 +01:00
parent 89ed7e25f7
commit 762cb6e317
8 changed files with 115 additions and 4 deletions

View file

@ -10,6 +10,7 @@ from c3nav.routing.graph.connection import GraphConnection
from c3nav.routing.graph.level import GraphLevel
from c3nav.routing.graph.point import GraphPoint
from c3nav.routing.graph.room import GraphRoom
from c3nav.routing.graph.router import Router
class Graph():
@ -25,6 +26,9 @@ class Graph():
self.rooms = []
self.levelconnector_points = {}
self.transfer_points = []
self.router = Router()
def build(self):
for level in self.levels.values():
level.build()
@ -88,9 +92,16 @@ class Graph():
graph = cls.unserialize(pickle.load(f))
return graph
def draw_pngs(self, points=True, lines=True):
def build_router(self):
for room in self.rooms:
room.build_router()
self.transfer_points.extend(room.router.transfer_points)
self.router.build(self.transfer_points, global_routing=True)
def draw_pngs(self, points=True, lines=True, transfer_points=False, transfer_lines=False):
for level in self.levels.values():
level.draw_png(points=points, lines=lines)
level.draw_png(points, lines, transfer_points, transfer_lines)
def add_levelconnector_point(self, levelconnector, point):
self.levelconnector_points.setdefault(levelconnector.name, []).append(point)

View file

@ -78,7 +78,7 @@ class GraphLevel():
print('%d points' % len(self.points))
print()
def draw_png(self, points=True, lines=True):
def draw_png(self, points=True, lines=True, transfer_points=False, transfer_lines=False):
filename = os.path.join(settings.RENDER_ROOT, 'level-%s.base.png' % self.level.name)
graph_filename = os.path.join(settings.RENDER_ROOT, 'level-%s.graph.png' % self.level.name)
@ -94,4 +94,15 @@ class GraphLevel():
for point in self.points:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (200, 0, 0))
if transfer_lines:
for point in self.points:
if point.in_room_transfer_distances is not None:
for otherpoint, distance in point.in_room_transfer_distances.items():
draw.line(_line_coords(point, otherpoint, height), fill=(100, 100, 255))
if transfer_points:
for point in self.points:
if point.in_room_transfer_distances is not None:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 200))
im.save(graph_filename)

View file

@ -10,6 +10,7 @@ class GraphPoint():
self.xy = (x, y)
self.connections = {}
self.connections_in = {}
self.in_room_transfer_distances = None
@cached_property
def ellipse_bbox(self):

View file

@ -6,6 +6,7 @@ from shapely.geometry import JOIN_STYLE, LineString
from c3nav.mapdata.utils import assert_multipolygon
from c3nav.routing.graph.point import GraphPoint
from c3nav.routing.graph.router import Router
from c3nav.routing.utils.coords import get_coords_angles
from c3nav.routing.utils.mpl import polygon_to_mpl_paths
@ -19,6 +20,8 @@ class GraphRoom():
self.clear_geometry = geometry.buffer(-0.3, join_style=JOIN_STYLE.mitre)
self.empty = self.clear_geometry.is_empty
self.router = Router()
if mpl_paths is not None:
self.mpl_paths = mpl_paths
elif not self.empty:
@ -63,6 +66,10 @@ class GraphRoom():
for from_point, to_point in permutations(points, 2):
from_point.connect_to(to_point)
# noinspection PyTypeChecker
def build_router(self):
self.router.build(self.points)
def _add_ring(self, geom, want_left):
"""
add the points of a ring, but only those that have a specific direction change.

View file

@ -0,0 +1,52 @@
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import shortest_path
class Router():
def __init__(self):
self.points = []
self.points_pk = None
self.transfer_points = set()
self.shortest_paths = None
self.predecessors = None
self._built = False
# noinspection PyTypeChecker
def build(self, points, global_routing=False):
if self._built:
raise RuntimeError('already built.')
self._built = True
self.points = points
self.points_pk = dict(zip(self.points, range(len(self.points))))
matrix = np.zeros((len(self.points), len(self.points)))
for point, pk in self.points_pk.items():
for to_point, connection in point.connections.items():
if to_point not in self.points_pk:
if not global_routing:
self.transfer_points.add(point)
continue
matrix[pk, self.points_pk[to_point]] = 1
if global_routing:
for to_point, distance in point.in_room_transfer_distances.items():
matrix[pk, self.points_pk[to_point]] = distance
g_sparse = csr_matrix(np.ma.masked_values(np.fromstring(matrix).reshape(matrix.shape), 0))
self.shortest_paths, self.predecessors = shortest_path(g_sparse, return_predecessors=True)
if not global_routing:
for from_point in self.transfer_points:
from_point.in_room_transfer_distances = {}
connections = self.shortest_paths[self.points_pk[from_point], ]
for to_point_pk in np.argwhere(connections != np.inf).flatten():
to_point = self.points[to_point_pk]
if to_point not in self.transfer_points:
continue
from_point.in_room_transfer_distances[to_point] = connections[to_point_pk]
def get_distance(self, from_point, to_point):
return self.shortest_paths[self.points_pk[from_point], self.points_pk[from_point]]

View file

@ -13,6 +13,15 @@ class Command(BaseCommand):
parser.add_argument('--no-lines', action='store_const', dest='lines', const=False, default=True,
help='dont draw lines on the graph image')
parser.add_argument('--transfer-points', action='store_const', const=True, default=False,
help='highlight transfer points')
parser.add_argument('--transfer-lines', action='store_const', const=True, default=False,
help='draw in-room transfer lines')
def handle(self, *args, **options):
graph = Graph.load()
graph.draw_pngs(points=options['points'], lines=options['lines'])
if options['transfer_points'] or options['transfer_lines']:
graph.build_router()
graph.draw_pngs(points=options['points'], lines=options['lines'],
transfer_points=options['transfer_points'], transfer_lines=options['transfer_lines'])

View file

@ -0,0 +1,19 @@
import time
from django.core.management.base import BaseCommand
from c3nav.routing.graph import Graph
class Command(BaseCommand):
help = 'check how long it takes to build the routers for the routing graph'
def handle(self, *args, **options):
start = time.time()
graph = Graph.load()
print('Graph loaded in %.4fs' % (time.time() - start))
start = time.time()
graph.build_router()
print('Routers built in %.4fs' % (time.time() - start))
print('%s transfer points' % len(graph.transfer_points))

View file

@ -9,3 +9,4 @@ celery>=3.1,<3.2
requests>=2.11,<2.12
Pillow>=3.4.2,<3.5
matplotlib>=1.5.3,<1.6
scipy>=0.18.1,<0.19