route from/to location groups and respect permissions

This commit is contained in:
Laura Klünder 2017-11-28 00:40:21 +01:00
parent e1192fccf7
commit b5f8cc3afd

View file

@ -3,6 +3,7 @@ import os
import pickle import pickle
from collections import deque from collections import deque
from functools import reduce from functools import reduce
from itertools import chain
import numpy as np import numpy as np
from django.conf import settings from django.conf import settings
@ -12,17 +13,18 @@ from shapely import prepared
from shapely.geometry import Point from shapely.geometry import Point
from shapely.ops import unary_union from shapely.ops import unary_union
from c3nav.mapdata.models import AltitudeArea, Area, GraphEdge, Level, Space, WayType from c3nav.mapdata.models import AltitudeArea, Area, GraphEdge, Level, LocationGroup, Space, WayType
from c3nav.routing.route import Route from c3nav.routing.route import Route
class Router: class Router:
filename = os.path.join(settings.CACHE_ROOT, 'router') filename = os.path.join(settings.CACHE_ROOT, 'router')
def __init__(self, levels, spaces, areas, nodes, edges, waytypes, graph): def __init__(self, levels, spaces, areas, groups, nodes, edges, waytypes, graph):
self.levels = levels self.levels = levels
self.spaces = spaces self.spaces = spaces
self.areas = areas self.areas = areas
self.groups = groups
self.nodes = nodes self.nodes = nodes
self.edges = edges self.edges = edges
self.waytypes = waytypes self.waytypes = waytypes
@ -34,20 +36,24 @@ class Router:
@classmethod @classmethod
def rebuild(cls): def rebuild(cls):
levels_query = Level.objects.prefetch_related('buildings', 'spaces', 'altitudeareas', levels_query = Level.objects.prefetch_related('buildings', 'spaces', 'altitudeareas', 'groups',
'spaces__holes', 'spaces__columns', 'spaces__holes', 'spaces__columns', 'spaces__groups',
'spaces__obstacles', 'spaces__lineobstacles', 'spaces__obstacles', 'spaces__lineobstacles',
'spaces__areas', 'spaces__graphnodes') 'spaces__graphnodes', 'spaces__areas', 'spaces__areas__groups')
levels = {} levels = {}
spaces = {} spaces = {}
areas = {} areas = {}
groups = {}
nodes = deque() nodes = deque()
for level in levels_query: for level in levels_query:
buildings_geom = unary_union(tuple(building.geometry for building in level.buildings.all())) buildings_geom = unary_union(tuple(building.geometry for building in level.buildings.all()))
nodes_before_count = len(nodes) nodes_before_count = len(nodes)
for group in level.groups.all():
groups.setdefault(group.pk, {}).setdefault('levels', set()).add(level.pk)
for space in level.spaces.all(): for space in level.spaces.all():
# create space geometries # create space geometries
accessible_geom = space.geometry.difference(unary_union( accessible_geom = space.geometry.difference(unary_union(
@ -61,6 +67,9 @@ class Router:
) )
# todo: do something with this, then remove #noqa # todo: do something with this, then remove #noqa
for group in space.groups.all():
groups.setdefault(group.pk, {}).setdefault('spaces', set()).add(space.pk)
space_nodes = tuple(RouterNode.from_graph_node(node, i) space_nodes = tuple(RouterNode.from_graph_node(node, i)
for i, node in enumerate(space.graphnodes.all())) for i, node in enumerate(space.graphnodes.all()))
for i, node in enumerate(space_nodes, start=len(nodes)): for i, node in enumerate(space_nodes, start=len(nodes)):
@ -68,6 +77,10 @@ class Router:
nodes.extend(space_nodes) nodes.extend(space_nodes)
for area in space.areas.all(): for area in space.areas.all():
for group in area.groups.all():
groups.setdefault(group.pk, {}).setdefault('areas', set()).add(area.pk)
area._prefetched_objects_cache = {}
area = RouterArea(area) area = RouterArea(area)
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)
@ -132,7 +145,7 @@ class Router:
waytype.upwards_indices = np.array(waytype.upwards_indices, dtype=np.uint32).reshape((-1, 2)) waytype.upwards_indices = np.array(waytype.upwards_indices, dtype=np.uint32).reshape((-1, 2))
waytype.nonupwards_indices = np.array(waytype.nonupwards_indices, dtype=np.uint32).reshape((-1, 2)) waytype.nonupwards_indices = np.array(waytype.nonupwards_indices, dtype=np.uint32).reshape((-1, 2))
router = cls(levels, spaces, areas, nodes, edges, waytypes, graph) router = cls(levels, spaces, areas, groups, nodes, edges, waytypes, graph)
pickle.dump(router, open(cls.filename, 'wb')) pickle.dump(router, open(cls.filename, 'wb'))
return router return router
@ -140,21 +153,32 @@ class Router:
def load(cls): def load(cls):
return pickle.load(open(cls.filename, 'rb')) return pickle.load(open(cls.filename, 'rb'))
def get_locations(self, location): def get_locations(self, location, permissions=frozenset()):
locations = ()
if isinstance(location, Level): if isinstance(location, Level):
return RouterLocation((self.levels[location.pk], )) locations = (self.levels.get(location.pk), )
if isinstance(location, Space): elif isinstance(location, Space):
return RouterLocation((self.spaces[location.pk], )) locations = (self.spaces.get(location.pk), )
if isinstance(location, Area): elif isinstance(location, Area):
return RouterLocation((self.areas[location.pk], )) locations = (self.areas.get(location.pk), )
elif isinstance(location, LocationGroup):
group = self.groups.get(location.pk)
locations = tuple(chain(
(self.levels[pk] for pk in group.get('levels', ())),
(self.spaces[pk] for pk in group.get('spaces', ())),
(self.areas[pk] for pk in group.get('areas', ()))
))
# todo: route from/to POI or custom location # todo: route from/to POI or custom location
# todo: route from/to location group return RouterLocation(tuple(location for location in locations
return RouterLocation() if location is not None and (location.access_restriction_id is None or
location.access_restriction_id in permissions)))
def get_route(self, origin, destination): def get_route(self, origin, destination, permissions=frozenset()):
# get possible origins and destinations # get possible origins and destinations
origins = self.get_locations(origin) origins = self.get_locations(origin, permissions=permissions)
destinations = self.get_locations(destination) destinations = self.get_locations(destination, permissions=permissions)
# 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 = shortest_path(self.graph, directed=True, return_predecessors=True)
@ -162,8 +186,10 @@ class Router:
# 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))
destination_nodes = np.array(tuple(destinations.nodes)) destination_nodes = np.array(tuple(destinations.nodes))
origin_node, destination_node = np.unravel_index(distances[origin_nodes, destination_nodes].argmin(), origin_node, destination_node = np.unravel_index(
(len(origin_nodes), len(destination_nodes))) distances[origin_nodes.reshape((-1, 1)), destination_nodes].argmin(),
(len(origin_nodes), len(destination_nodes))
)
origin_node = origin_nodes[origin_node] origin_node = origin_nodes[origin_node]
destination_node = destination_nodes[destination_node] destination_node = destination_nodes[destination_node]