route from point to point and describe points

This commit is contained in:
Laura Klünder 2016-12-21 00:24:56 +01:00
parent 8a9406275b
commit bbfca9f318
9 changed files with 127 additions and 19 deletions

View file

@ -3,7 +3,7 @@ from collections import OrderedDict
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from shapely.geometry import CAP_STYLE, JOIN_STYLE
from shapely.geometry import CAP_STYLE, JOIN_STYLE, Point
from shapely.geometry.geo import mapping, shape
from c3nav.mapdata.fields import GeometryField
@ -68,6 +68,9 @@ class GeometryMapItem(MapItem, metaclass=GeometryMapItemMeta):
def get_shadow_geojson(self):
return None
def contains(self, x, y):
return self.geometry.contains(Point(x, y))
class GeometryMapItemWithLevel(GeometryMapItem):
"""

View file

@ -172,8 +172,14 @@ class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
def subtitle(self):
return self.get_subtitle()
def get_subtitle(self):
items = [self.get_location_type_display()]
@property
def subtitle_without_type(self):
return self.get_subtitle(with_type=False)
def get_subtitle(self, with_type=True):
items = []
if with_type:
items += [self.get_location_type_display()]
items += [area.title for area in self.get_in_areas()]
return ', '.join(items)
@ -284,13 +290,37 @@ class PointLocation(Location):
def location_id(self):
return 'c:%s:%d:%d' % (self.level.name, self.x*100, self.y*100)
@cached_property
def description(self):
from c3nav.routing.graph import Graph
graph = Graph.load()
point = graph.get_nearest_point(self.level, self.x, self.y)
if point is None:
return _('Unreachable Coordinates'), ''
locations = sorted(AreaLocation.objects.filter(name__in=point.arealocations, can_describe=True),
key=AreaLocation.get_sort_key, reverse=True)
if not locations:
return _('Coordinates'), ''
location = locations[0]
if location.contains(self.x, self.y):
return (_('Coordinates in %(location)s') % {'location': location.title}), location.subtitle_without_type
else:
return (_('Coordinates near %(location)s') % {'location': location.title}), location.subtitle_without_type
@property
def title(self) -> str:
return 'Custom location'
return self.description[0]
@property
def subtitle(self) -> str:
return 'Coordinates'
add_subtitle = self.description[1]
subtitle = '%s:%d:%d' % (self.level.name, self.x*100, self.y*100)
if add_subtitle:
subtitle += ' - '+add_subtitle
return subtitle
def to_json(self):
result = super().to_location_json()

View file

@ -31,7 +31,7 @@ class GraphArea():
def build_connections(self):
for point1, point2 in combinations(self._built_points, 2):
there, back = self.check_connection(point1, point2)
there, back = self.check_connection(point1.xy, point2.xy)
if there is not None:
point1.connect_to(point2, ctype=there)
@ -40,14 +40,14 @@ class GraphArea():
point2.connect_to(point1, ctype=back)
def check_connection(self, point1, point2):
path = Path(np.vstack((point1.xy, point2.xy)))
path = Path(np.vstack((point1, point2)))
# lies within room
if self.mpl_clear.intersects_path(path):
return None, None
# stair checker
angle = coord_angle(point1.xy, point2.xy)
angle = coord_angle(point1, point2)
stair_direction_up = None
for stair_path, stair_angle in self.mpl_stairs:
if not path.intersects_path(stair_path):
@ -65,7 +65,7 @@ class GraphArea():
return None, None
# escalator checker
angle = coord_angle(point1.xy, point2.xy)
angle = coord_angle(point1, point2)
escalator_direction_up = None
escalator_swap_direction = False
for escalator in self.escalators:
@ -102,3 +102,18 @@ class GraphArea():
def finish_build(self):
self.points = np.array(tuple(point.i for point in self._built_points))
def contains_point(self, point):
return self.mpl_clear.contains_point(point)
def connected_points(self, point, mode):
connections = {}
for point_i in self.points:
other_point = self.graph.points[point_i]
there, back = self.check_connection(point, other_point.xy)
ctype = there if mode == 'orig' else back
if ctype is not None:
distance = np.linalg.norm(point - other_point.xy)
connections[point_i] = (distance, ctype)
return connections

View file

@ -240,14 +240,19 @@ class Graph:
routers[self] = GraphRouter(shortest_paths, predecessors, level_transfers)
return routers
def get_location_points(self, location: Location):
def get_location_points(self, location: Location, mode):
if isinstance(location, PointLocation):
return self.levels[level.name].points_from()
return 'bla'
points = self.levels[location.level.name].connected_points(np.array((location.x, location.y)), mode)
points, distances = zip(*((point_i, distance) for point_i, (distance, ctype) in points.items()))
points = np.array(points)
distances = np.array(distances)
return points, distances
elif isinstance(location, AreaLocation):
return self.levels[location.level.name].arealocation_points[location.name]
points = self.levels[location.level.name].arealocation_points[location.name]
return points, None
elif isinstance(location, LocationGroup):
return np.hstack(tuple(self.get_location_points(area) for area in location.locationareas))
points = set(np.hstack(tuple(self.get_location_points(area) for area in location.locationareas)))
return points, None
def _get_points_by_i(self, points):
return tuple(self.points[i] for i in points)
@ -256,8 +261,8 @@ class Graph:
return np.array(tuple(i for i, point in enumerate(points) if point in allowed_points_i))
def get_route(self, origin: Location, destination: Location, allowed_ctypes, public, nonpublic, avoid, include):
orig_points_i = set(self.get_location_points(origin))
dest_points_i = set(self.get_location_points(destination))
orig_points_i, orig_distances = self.get_location_points(origin, 'orig')
dest_points_i, dest_distances = self.get_location_points(destination, 'dest')
orig_points = self._get_points_by_i(orig_points_i)
dest_points = self._get_points_by_i(dest_points_i)
@ -276,6 +281,15 @@ class Graph:
orig_room_points = {room: self._allowed_points_index(room.points, orig_points_i) for room in orig_rooms}
dest_room_points = {room: self._allowed_points_index(room.points, dest_points_i) for room in dest_rooms}
# add distances to room routers
if orig_distances is not None:
for room in orig_rooms:
routers[room].shortest_paths[orig_room_points[room], :] += orig_distances[:, None]
if dest_distances is not None:
for room in dest_rooms:
routers[room].shortest_paths[:, dest_room_points[room]] += dest_distances
# if the points have common rooms, search for routes within those rooms
if common_rooms:
for room in common_rooms:
@ -439,5 +453,8 @@ class Graph:
return level_transfers
def get_nearest_point(self, level, x, y):
return self.levels[level.name].nearest_point(np.array((x, y)), 'orig')
GraphRouter = namedtuple('GraphRouter', ('shortest_paths', 'predecessors', 'level_transfers', ))

View file

@ -3,6 +3,7 @@ from collections import namedtuple
import numpy as np
from django.conf import settings
from django.core.cache import cache
from matplotlib.path import Path
from PIL import Image, ImageDraw
from scipy.sparse.csgraph._shortest_path import shortest_path
@ -383,6 +384,38 @@ class GraphLevel():
routers[self] = LevelRouter(shortest_paths, predecessors, room_transfers)
return routers
def nearest_point(self, point, mode):
cache_key = ('c3nav__routing__nearest_point__%s__%s__%.2f_%.2f__%s' %
(self.graph.mtime, self.level.name, point[0], point[1], mode))
points = cache.get(cache_key, None)
if points is None:
points = self._nearest_point(point, mode)
cache.set(cache_key, points, 60)
return points
def _nearest_point(self, point, mode):
points = self.connected_points(point, mode)
if not points:
return None
nearest_point = min(points.items(), key=lambda x: x[1][0])
return self.graph.points[nearest_point[0]]
def connected_points(self, point, mode):
cache_key = ('c3nav__routing__connected_points__%s__%s__%.2f_%.2f__%s' %
(self.graph.mtime, self.level.name, point[0], point[1], mode))
points = cache.get(cache_key, None)
if points is None:
points = self._connected_points(point, mode)
cache.set(cache_key, points, 60)
return points
def _connected_points(self, point, mode):
for room in self.rooms:
if room.contains_point(point):
return room.connected_points(point, mode)
return {}
LevelRouter = namedtuple('LevelRouter', ('shortest_paths', 'predecessors', 'room_transfers', ))
EscalatorData = namedtuple('EscalatorData', ('mpl_geom', 'direction_up', 'slope', 'angle'))

View file

@ -294,5 +294,15 @@ class GraphRoom():
return GraphConnection(self.graph.points[self.points[from_i]], self.graph.points[self.points[to_i]],
distance=distance, ctype=ctype)
def contains_point(self, point):
return self.mpl_clear.contains_point(point)
def connected_points(self, point, mode):
connections = {}
for area in self.areas:
if area.contains_point(point):
connections.update(area.connected_points(point, mode))
return connections
RoomRouter = namedtuple('RoomRouter', ('shortest_paths', 'predecessors', ))

View file

@ -54,7 +54,7 @@ class Route:
@staticmethod
def describe_point(point):
locations = sorted(AreaLocation.objects.filter(location_type__in=('room', 'level', 'area'),
name__in=point.arealocations),
name__in=point.arealocations, can_describe=True),
key=AreaLocation.get_sort_key, reverse=True)
if not locations:

View file

@ -84,7 +84,7 @@ c3nav = {
},
_locationselect_click_image: function(e) {
var level = $(e.delegateTarget).attr('data-level');
var coords = 'c:'+level+':'+parseInt(e.offsetX/6*100)+':'+parseInt(e.offsetY/6*100);
var coords = 'c:'+level+':'+parseInt(e.offsetX/6*100)+':'+parseInt((c3nav.svg_height-e.offsetY)/6*100);
var location_group = $(this).closest('.location-group');
location_group.removeClass('map').addClass('selected');
var selected = location_group.find('.locationselect-selected');

View file

@ -87,7 +87,7 @@ def main(request, location=None, origin=None, destination=None):
x = request.POST.get('x')
y = request.POST.get('y')
if x.isnumeric() and y.isnumeric():
coords = 'c:%s:%d:%d' % (map_level, int(int(x)/6*100), int(int(y)/6*100))
coords = 'c:%s:%d:%d' % (map_level, int(int(x)/6*100), height-int(int(y)/6*100))
if active_field == 'origin':
return redirect('site.route', origin=coords, destination=destination.location_id)
elif active_field == 'destination':