route from/to location groups and respect permissions
This commit is contained in:
parent
e1192fccf7
commit
b5f8cc3afd
1 changed files with 45 additions and 19 deletions
|
@ -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]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue