speed up router

This commit is contained in:
Laura Klünder 2017-11-29 00:01:23 +01:00
parent f260b52a6e
commit c71b9b0f8b

View file

@ -1,6 +1,7 @@
import operator import operator
import os import os
import pickle import pickle
import threading
from collections import deque from collections import deque
from functools import reduce from functools import reduce
from itertools import chain from itertools import chain
@ -15,7 +16,7 @@ from shapely.ops import unary_union
from c3nav.mapdata.models import AltitudeArea, Area, GraphEdge, Level, LocationGroup, Space, WayType from c3nav.mapdata.models import AltitudeArea, Area, GraphEdge, Level, LocationGroup, Space, WayType
from c3nav.mapdata.models.geometry.space import POI from c3nav.mapdata.models.geometry.space import POI
from c3nav.mapdata.utils.geometry import assert_multipolygon, good_representative_point from c3nav.mapdata.utils.geometry import assert_multipolygon, get_rings, good_representative_point
from c3nav.mapdata.utils.locations import CustomLocation from c3nav.mapdata.utils.locations import CustomLocation
from c3nav.routing.route import Route from c3nav.routing.route import Route
@ -71,7 +72,7 @@ class Router:
tuple(obstacle.geometry for obstacle in space.obstacles.all()) + tuple(obstacle.geometry for obstacle in space.obstacles.all()) +
tuple(lineobstacle.buffered_geometry for lineobstacle in space.lineobstacles.all()) tuple(lineobstacle.buffered_geometry for lineobstacle in space.lineobstacles.all())
) )
clear_geom = accessible_geom.difference(obstacles_geom) clear_geom = unary_union(tuple(get_rings(accessible_geom.difference(obstacles_geom))))
clear_geom_prep = prepared.prep(clear_geom) clear_geom_prep = prepared.prep(clear_geom)
for group in space.groups.all(): for group in space.groups.all():
@ -103,7 +104,8 @@ class Router:
if not space.geometry_prep.intersects(area.geometry): if not space.geometry_prep.intersects(area.geometry):
continue continue
for subgeom in assert_multipolygon(accessible_geom.intersection(area.geometry)): for subgeom in assert_multipolygon(accessible_geom.intersection(area.geometry)):
area = RouterAltitudeArea(subgeom, subgeom.difference(obstacles_geom), area_clear_geom = unary_union(tuple(get_rings(subgeom.difference(obstacles_geom))))
area = RouterAltitudeArea(subgeom, area_clear_geom,
area.altitude, area.altitude2, area.point1, area.point2) area.altitude, area.altitude2, area.point1, area.point2)
area_nodes = tuple(node for node in space_nodes if area.geometry_prep.intersects(node.point)) area_nodes = tuple(node for node in space_nodes if area.geometry_prep.intersects(node.point))
area.nodes = set(node.i for node in area_nodes) area.nodes = set(node.i for node in area_nodes)
@ -123,7 +125,7 @@ class Router:
# todo: check waytypes here # todo: check waytypes here
for node in space_nodes: for node in space_nodes:
line = LineString([(node.x, node.y), (fallback_node.x, fallback_node.y)]) line = LineString([(node.x, node.y), (fallback_node.x, fallback_node.y)])
if line.length < 5 and not clear_geom_prep.crosses(line): if line.length < 5 and not clear_geom_prep.intersects(line):
area.fallback_nodes[node.i] = ( area.fallback_nodes[node.i] = (
fallback_node, fallback_node,
RouterEdge(fallback_node, node, 0) RouterEdge(fallback_node, node, 0)
@ -192,9 +194,23 @@ class Router:
return router return router
@classmethod @classmethod
def load(cls): def load_nocache(cls):
return pickle.load(open(cls.filename, 'rb')) return pickle.load(open(cls.filename, 'rb'))
cached = None
cache_key = None
cache_lock = threading.Lock()
@classmethod
def load(cls):
from c3nav.mapdata.models import MapUpdate
cache_key = MapUpdate.current_processed_cache_key()
if cls.cache_key != cache_key:
with cls.cache_lock:
cls.cache_key = cache_key
cls.cached = cls.load_nocache()
return cls.cached
def get_locations(self, location, permissions=frozenset()): def get_locations(self, location, permissions=frozenset()):
locations = () locations = ()
if isinstance(location, Level): if isinstance(location, Level):
@ -231,10 +247,14 @@ class Router:
point = Point(point.x, point.y) point = Point(point.x, point.y)
level = self.levels[level] level = self.levels[level]
for space in level.spaces: for space in level.spaces:
if self.spaces[space].geometry_prep.intersects(point): if self.spaces[space].geometry_prep.contains(point):
return self.spaces[space] return self.spaces[space]
return self.spaces[min(level.spaces, key=lambda space: self.spaces[space].geometry.distance(point))] return self.spaces[min(level.spaces, key=lambda space: self.spaces[space].geometry.distance(point))]
@cached_property
def shortest_path(self):
return shortest_path(self.graph, directed=True, return_predecessors=True)
def get_route(self, origin, destination, permissions=frozenset()): def get_route(self, origin, destination, permissions=frozenset()):
# get possible origins and destinations # get possible origins and destinations
origins = self.get_locations(origin, permissions=permissions) origins = self.get_locations(origin, permissions=permissions)
@ -243,7 +263,7 @@ class Router:
# todo: throw error if route is impossible # todo: throw error if route is impossible
# calculate shortest path matrix # calculate shortest path matrix
distances, predecessors = shortest_path(self.graph, directed=True, return_predecessors=True) distances, predecessors = self.shortest_path
# find shortest path for our origins and destinations # find shortest path for our origins and destinations
origin_nodes = np.array(tuple(origins.nodes)) origin_nodes = np.array(tuple(origins.nodes))
@ -269,7 +289,6 @@ class Router:
origin_addition = origin.nodes_addition.get(origin_node) origin_addition = origin.nodes_addition.get(origin_node)
destination_addition = destination.nodes_addition.get(destination_node) destination_addition = destination.nodes_addition.get(destination_node)
print(origin_addition, destination_addition)
return Route(self, origin, destination, distances[origin_node, destination_node], path_nodes, return Route(self, origin, destination, distances[origin_node, destination_node], path_nodes,
origin_addition, destination_addition) origin_addition, destination_addition)
@ -354,7 +373,7 @@ class RouterAltitudeArea:
for node in self.nodes: for node in self.nodes:
node = all_nodes[node] node = all_nodes[node]
line = LineString([(node.x, node.y), (point.x, point.y)]) line = LineString([(node.x, node.y), (point.x, point.y)])
if line.length < 5 and not self.clear_geometry_prep.crosses(line): if line.length < 5 and not self.clear_geometry_prep.intersects(line):
nodes[node.i] = (None, None) nodes[node.i] = (None, None)
if not nodes: if not nodes:
nearest_node = min(tuple(all_nodes[node] for node in self.nodes), nearest_node = min(tuple(all_nodes[node] for node in self.nodes),