add graph router
This commit is contained in:
parent
89ed7e25f7
commit
762cb6e317
8 changed files with 115 additions and 4 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
|
|
52
src/c3nav/routing/graph/router.py
Normal file
52
src/c3nav/routing/graph/router.py
Normal 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]]
|
|
@ -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'])
|
||||
|
|
19
src/c3nav/routing/management/commands/testgraph.py
Normal file
19
src/c3nav/routing/management/commands/testgraph.py
Normal 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))
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue