route from/to custom locations

This commit is contained in:
Laura Klünder 2017-11-28 23:08:47 +01:00
parent f5ee24495e
commit b92c871bc2
5 changed files with 39 additions and 17 deletions

View file

@ -69,6 +69,8 @@ def assert_multilinestring(geometry: Union[LineString, MultiLineString, Geometry
def good_representative_point(geometry): def good_representative_point(geometry):
if isinstance(geometry, Point):
return geometry
c = geometry.centroid c = geometry.centroid
x1, y1, x2, y2 = geometry.bounds x1, y1, x2, y2 = geometry.bounds
lines = (tuple(assert_multilinestring(LineString(((x1, c.y), (x2, c.y))).intersection(geometry))) + lines = (tuple(assert_multilinestring(LineString(((x1, c.y), (x2, c.y))).intersection(geometry))) +

View file

@ -227,6 +227,8 @@ def get_custom_location_for_request(slug: str, request):
class CustomLocation: class CustomLocation:
can_search = True can_search = True
can_describe = True
access_restriction_id = None
def __init__(self, level, x, y): def __init__(self, level, x, y):
x = round(x, 2) x = round(x, 2)

View file

@ -1,25 +1,25 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from c3nav.mapdata.utils.locations import locations_for_request from c3nav.mapdata.utils.locations import get_location_by_id_for_request
class RouteForm(forms.Form): class RouteForm(forms.Form):
origin = forms.IntegerField(min_value=1) origin = forms.CharField()
destination = forms.IntegerField(min_value=1) destination = forms.CharField()
def __init__(self, *args, request=None, **kwargs): def __init__(self, *args, request=None, **kwargs):
self.request = request self.request = request
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def clean_origin(self): def clean_origin(self):
try: location = get_location_by_id_for_request(self.cleaned_data['origin'], self.request)
return locations_for_request(self.request)[self.cleaned_data['origin']] if location is None:
except KeyError:
raise forms.ValidationError(ugettext_lazy('Unknown origin.')) raise forms.ValidationError(ugettext_lazy('Unknown origin.'))
return location
def clean_destination(self): def clean_destination(self):
try: location = get_location_by_id_for_request(self.cleaned_data['destination'], self.request)
return locations_for_request(self.request)[self.cleaned_data['destination']] if location is None:
except KeyError:
raise forms.ValidationError(ugettext_lazy('Unknown destination.')) raise forms.ValidationError(ugettext_lazy('Unknown destination.'))
return location

View file

@ -16,6 +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, good_representative_point
from c3nav.mapdata.utils.locations import CustomLocation
from c3nav.routing.route import Route from c3nav.routing.route import Route
@ -122,7 +123,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 not clear_geom_prep.crosses(line): if line.length < 5 and not clear_geom_prep.crosses(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)
@ -139,10 +140,9 @@ class Router:
groups.setdefault(group.pk, {}).setdefault('pois', set()).add(poi.pk) groups.setdefault(group.pk, {}).setdefault('pois', set()).add(poi.pk)
poi._prefetched_objects_cache = {} poi._prefetched_objects_cache = {}
poi = RouterPOI(poi) poi = RouterPoint(poi)
altitudearea = space.altitudearea_for_point(poi.geometry) altitudearea = space.altitudearea_for_point(poi.geometry)
poi_nodes = altitudearea.nodes_for_point(poi.geometry, all_nodes=nodes) poi_nodes = altitudearea.nodes_for_point(poi.geometry, all_nodes=nodes)
print(poi_nodes)
poi.nodes = set(i for i in poi_nodes.keys()) poi.nodes = set(i for i in poi_nodes.keys())
poi.nodes_addition = poi_nodes poi.nodes_addition = poi_nodes
pois[poi.pk] = poi pois[poi.pk] = poi
@ -212,11 +212,29 @@ class Router:
(self.spaces[pk] for pk in group.get('spaces', ())), (self.spaces[pk] for pk in group.get('spaces', ())),
(self.areas[pk] for pk in group.get('areas', ())) (self.areas[pk] for pk in group.get('areas', ()))
)) ))
elif isinstance(location, CustomLocation):
point = Point(location.x, location.y)
location = RouterPoint(location)
space = self.space_for_point(location.level.pk, point)
altitudearea = space.altitudearea_for_point(point)
location_nodes = altitudearea.nodes_for_point(point, all_nodes=self.nodes)
location.nodes = set(i for i in location_nodes.keys())
location.nodes_addition = location_nodes
locations = tuple((location, ))
# todo: route from/to custom location # todo: route from/to custom location
return RouterLocation(tuple(location for location in locations return RouterLocation(tuple(location for location in locations
if location is not None and (location.access_restriction_id is None or if location is not None and (location.access_restriction_id is None or
location.access_restriction_id in permissions))) location.access_restriction_id in permissions)))
def space_for_point(self, level, point):
# todo: only spaces that the user can see
point = Point(point.x, point.y)
level = self.levels[level]
for space in level.spaces:
if self.spaces[space].geometry_prep.intersects(point):
return self.spaces[space]
return self.spaces[min(level.spaces, key=lambda space: self.spaces[space].geometry.distance(point))]
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)
@ -289,14 +307,14 @@ class RouterSpace(BaseRouterProxy):
for area in self.altitudeareas: for area in self.altitudeareas:
if area.geometry_prep.intersects(point): if area.geometry_prep.intersects(point):
return area return area
return self.altitudareas[0] return min(self.altitudeareas, key=lambda area: area.geometry.distance(point))
class RouterArea(BaseRouterProxy): class RouterArea(BaseRouterProxy):
pass pass
class RouterPOI(BaseRouterProxy): class RouterPoint(BaseRouterProxy):
pass pass
@ -331,7 +349,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 not self.clear_geometry_prep.crosses(line): if line.length < 5 and not self.clear_geometry_prep.crosses(line):
nodes[node.i] = None nodes[node.i] = 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),
@ -394,7 +412,7 @@ class RouterWayType:
class RouterLocation: class RouterLocation:
def __init__(self, locations=()): def __init__(self, locations=()):
self.locations = tuple(location for location in locations if locations) self.locations = locations
@cached_property @cached_property
def nodes(self): def nodes(self):

View file

@ -218,7 +218,7 @@ c3nav = {
}, },
_route_loaded: function(data, nofly) { _route_loaded: function(data, nofly) {
var $route = $('#route-summary'); var $route = $('#route-summary');
if (parseInt($route.attr('data-origin')) !== data.request.origin || parseInt($route.attr('data-destination')) !== data.request.destination) { if ($route.attr('data-origin') !== String(data.request.origin) || $route.attr('data-destination') !== String(data.request.destination)) {
// loaded too late, information no longer needed // loaded too late, information no longer needed
return; return;
} }