show nearby locations

This commit is contained in:
Laura Klünder 2019-12-25 00:40:19 +01:00
parent 3134829cc7
commit 27b35a384d
7 changed files with 68 additions and 19 deletions

View file

@ -283,6 +283,7 @@ class CustomLocation:
('grid_square', self.grid_square), ('grid_square', self.grid_square),
('near_area', self.near_area.pk if self.near_area else None), ('near_area', self.near_area.pk if self.near_area else None),
('near_poi', self.near_poi.pk if self.near_poi else None), ('near_poi', self.near_poi.pk if self.near_poi else None),
('nearby', tuple(location.pk for location in self.nearby)),
('altitude', None if self.altitude is None else round(self.altitude, 2)) ('altitude', None if self.altitude is None else round(self.altitude, 2))
)) ))
if not grid.enabled: if not grid.enabled:
@ -378,6 +379,10 @@ class CustomLocation:
def near_poi(self): def near_poi(self):
return self.description.near_poi return self.description.near_poi
@cached_property
def nearby(self):
return self.description.nearby
@cached_property @cached_property
def grid_square(self): def grid_square(self):
return grid.get_square_for_point(self.x, self.y) or '' return grid.get_square_for_point(self.x, self.y) or ''

View file

@ -363,15 +363,25 @@ class Router:
restrictions = self.get_restrictions(location.permissions) restrictions = self.get_restrictions(location.permissions)
space = self.space_for_point(level=location.level.pk, point=location, restrictions=restrictions) space = self.space_for_point(level=location.level.pk, point=location, restrictions=restrictions)
if not space: if not space:
return CustomLocationDescription(space=space, altitude=None, areas=(), near_area=None, near_poi=None) return CustomLocationDescription(space=space, altitude=None, areas=(), near_area=None, near_poi=None,
nearby=())
try: try:
altitude = space.altitudearea_for_point(location).get_altitude(location) altitude = space.altitudearea_for_point(location).get_altitude(location)
except LocationUnreachable: except LocationUnreachable:
altitude = None altitude = None
areas, near_area = space.areas_for_point(areas=self.areas, point=location, restrictions=restrictions) areas, near_area, nearby_areas = space.areas_for_point(
near_poi = space.poi_for_point(pois=self.pois, point=location, restrictions=restrictions) areas=self.areas, point=location, restrictions=restrictions
)
near_poi, nearby_pois = space.poi_for_point(
pois=self.pois, point=location, restrictions=restrictions
)
nearby = tuple(sorted(
tuple(l for l in nearby_areas+nearby_pois if l[0].can_search),
key=operator.itemgetter(1)
))[:20]
nearby = tuple(location for location, distance in nearby)
return CustomLocationDescription(space=space, altitude=altitude, return CustomLocationDescription(space=space, altitude=altitude,
areas=areas, near_area=near_area, near_poi=near_poi) areas=areas, near_area=near_area, near_poi=near_poi, nearby=nearby)
def shortest_path(self, restrictions, options): def shortest_path(self, restrictions, options):
options_key = options.serialize_string() options_key = options.serialize_string()
@ -482,7 +492,7 @@ class Router:
CustomLocationDescription = namedtuple('CustomLocationDescription', ('space', 'altitude', CustomLocationDescription = namedtuple('CustomLocationDescription', ('space', 'altitude',
'areas', 'near_area', 'near_poi')) 'areas', 'near_area', 'near_poi', 'nearby'))
class BaseRouterProxy: class BaseRouterProxy:
@ -535,26 +545,30 @@ class RouterSpace(BaseRouterProxy):
areas = {pk: area for pk, area in areas.items() areas = {pk: area for pk, area in areas.items()
if pk in self.areas and area.can_describe and area.access_restriction_id not in restrictions} if pk in self.areas and area.can_describe and area.access_restriction_id not in restrictions}
nearby = ((area, area.geometry.distance(point)) for area in areas.values())
nearby = tuple((area, distance) for area, distance in nearby if distance < 20)
contained = tuple(area for area in areas.values() if area.geometry_prep.contains(point)) contained = tuple(area for area in areas.values() if area.geometry_prep.contains(point))
if contained: if contained:
return tuple(sorted(contained, key=lambda area: area.geometry.area)), None return tuple(sorted(contained, key=lambda area: area.geometry.area)), None, nearby
near = ((area, area.geometry.distance(point)) for area in areas.values()) near = tuple((area, distance) for area, distance in nearby if distance < 5)
near = tuple((area, distance) for area, distance in near if distance < 5)
if not near: if not near:
return (), None return (), None, nearby
return (), min(near, key=operator.itemgetter(1))[0] return (), min(near, key=operator.itemgetter(1))[0], nearby
def poi_for_point(self, pois, point, restrictions): def poi_for_point(self, pois, point, restrictions):
point = Point(point.x, point.y) point = Point(point.x, point.y)
pois = {pk: poi for pk, poi in pois.items() pois = {pk: poi for pk, poi in pois.items()
if pk in self.pois and poi.can_describe and poi.access_restriction_id not in restrictions} if pk in self.pois and poi.can_describe and poi.access_restriction_id not in restrictions}
near = ((poi, poi.geometry.distance(point)) for poi in pois.values()) nearby = ((poi, poi.geometry.distance(point)) for poi in pois.values())
near = tuple((poi, distance) for poi, distance in near if distance < 5) nearby = tuple((poi, distance) for poi, distance in nearby if distance < 20)
near = tuple((poi, distance) for poi, distance in nearby if distance < 5)
if not near: if not near:
return None return None, nearby
return min(near, key=operator.itemgetter(1))[0] return min(near, key=operator.itemgetter(1))[0], nearby
class RouterArea(BaseRouterProxy): class RouterArea(BaseRouterProxy):

View file

@ -213,13 +213,18 @@ c3nav = {
}, },
state: {}, state: {},
update_state: function(routing, replace, details, options) { update_state: function(routing, replace, details, options, nearby) {
if (typeof routing !== "boolean") routing = c3nav.state.routing; if (typeof routing !== "boolean") routing = c3nav.state.routing;
if (details) { if (details) {
options = false; options = false;
nearby = false;
} else if (options) { } else if (options) {
details = false; details = false;
nearby = false;
} else if (nearby) {
details = false;
options = false;
} }
var destination = $('#destination-input').data('location'), var destination = $('#destination-input').data('location'),
@ -230,7 +235,8 @@ c3nav = {
destination: destination, destination: destination,
sidebar: true, sidebar: true,
details: !!details, details: !!details,
options: !!options options: !!options,
nearby: !!nearby,
}; };
c3nav._push_state(new_state, replace); c3nav._push_state(new_state, replace);
@ -669,6 +675,9 @@ c3nav = {
if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) { if (state.details && (url.startsWith('/l/') || url.startsWith('/r/'))) {
url += 'details/' url += 'details/'
} }
if (state.nearby && url.startsWith('/l/')) {
url += 'nearby/'
}
if (state.options && url.startsWith('/r/')) { if (state.options && url.startsWith('/r/')) {
url += 'options/' url += 'options/'
} }
@ -1212,6 +1221,7 @@ c3nav = {
L.Icon.Default.imagePath = '/static/leaflet/images/'; L.Icon.Default.imagePath = '/static/leaflet/images/';
c3nav._add_icon('origin'); c3nav._add_icon('origin');
c3nav._add_icon('destination'); c3nav._add_icon('destination');
c3nav._add_icon('nearby');
// setup scale control // setup scale control
L.control.scale({imperial: false}).addTo(c3nav.map); L.control.scale({imperial: false}).addTo(c3nav.map);
@ -1281,7 +1291,7 @@ c3nav = {
if (nearby) { if (nearby) {
var $destination = $('#destination-input'); var $destination = $('#destination-input');
c3nav._locationinput_set($destination, data); c3nav._locationinput_set($destination, data);
c3nav.update_state(false); c3nav.update_state(false, false, false, false, true);
} else { } else {
newpopup = L.popup(c3nav._add_map_padding({ newpopup = L.popup(c3nav._add_map_padding({
className: 'location-popup', className: 'location-popup',
@ -1333,6 +1343,24 @@ c3nav = {
c3nav._visible_map_locations = []; c3nav._visible_map_locations = [];
if (origin) c3nav._merge_bounds(bounds, c3nav._add_location_to_map(origin, single ? new L.Icon.Default() : c3nav.originIcon)); if (origin) c3nav._merge_bounds(bounds, c3nav._add_location_to_map(origin, single ? new L.Icon.Default() : c3nav.originIcon));
if (destination) c3nav._merge_bounds(bounds, c3nav._add_location_to_map(destination, single ? new L.Icon.Default() : c3nav.destinationIcon)); if (destination) c3nav._merge_bounds(bounds, c3nav._add_location_to_map(destination, single ? new L.Icon.Default() : c3nav.destinationIcon));
var done = [];
if (c3nav.state.nearby && destination && 'areas' in destination) {
if (destination.space) {
c3nav._merge_bounds(bounds, c3nav._add_location_to_map(c3nav.locations_by_id[destination.space], c3nav.nearbyIcon, true));
}
if (destination.near_area) {
done.push(destination.near_area);
c3nav._merge_bounds(bounds, c3nav._add_location_to_map(c3nav.locations_by_id[destination.near_area], c3nav.nearbyIcon, true));
}
for (var area of destination.areas) {
done.push(area);
c3nav._merge_bounds(bounds, c3nav._add_location_to_map(c3nav.locations_by_id[area], c3nav.nearbyIcon, true));
}
for (var location of destination.nearby) {
if (location in done) continue;
c3nav._merge_bounds(bounds, c3nav._add_location_to_map(c3nav.locations_by_id[location], c3nav.nearbyIcon, true));
}
}
c3nav._locationLayerBounds = bounds; c3nav._locationLayerBounds = bounds;
}, },
fly_to_bounds: function(replace_state, nofly) { fly_to_bounds: function(replace_state, nofly) {

View file

@ -8,12 +8,13 @@ slug = r'(?P<slug>[a-z0-9-_.:]+)'
coordinates = r'(?P<coordinates>[a-z0-9-_:]+:-?\d+(\.\d+)?:-?\d+(\.\d+)?)' coordinates = r'(?P<coordinates>[a-z0-9-_:]+:-?\d+(\.\d+)?:-?\d+(\.\d+)?)'
slug2 = r'(?P<slug2>[a-z0-9-_.:]+)' slug2 = r'(?P<slug2>[a-z0-9-_.:]+)'
details = r'(?P<details>details/)?' details = r'(?P<details>details/)?'
nearby = r'(?P<nearby>nearby/)?'
options = r'(?P<options>options/)?' options = r'(?P<options>options/)?'
pos = r'(@(?P<level>[a-z0-9-_:]+),(?P<x>-?\d+(\.\d+)?),(?P<y>-?\d+(\.\d+)?),(?P<zoom>-?\d+(\.\d+)?))?' pos = r'(@(?P<level>[a-z0-9-_:]+),(?P<x>-?\d+(\.\d+)?),(?P<y>-?\d+(\.\d+)?),(?P<zoom>-?\d+(\.\d+)?))?'
embed = r'(?P<embed>embed/)?' embed = r'(?P<embed>embed/)?'
urlpatterns = [ urlpatterns = [
url(r'^%s(?P<mode>[l])/%s/%s%s$' % (embed, slug, details, pos), map_index, name='site.index'), url(r'^%s(?P<mode>[l])/%s/(%s|%s)%s$' % (embed, slug, details, nearby, pos), map_index, name='site.index'),
url(r'^%s(?P<mode>[od])/%s/%s$' % (embed, slug, pos), map_index, name='site.index'), url(r'^%s(?P<mode>[od])/%s/%s$' % (embed, slug, pos), map_index, name='site.index'),
url(r'^%sr/%s/%s/(%s|%s)%s$' % (embed, slug, slug2, details, options, pos), map_index, name='site.index'), url(r'^%sr/%s/%s/(%s|%s)%s$' % (embed, slug, slug2, details, options, pos), map_index, name='site.index'),
url(r'^%s(?P<mode>r)/%s$' % (embed, pos), map_index, name='site.index'), url(r'^%s(?P<mode>r)/%s$' % (embed, pos), map_index, name='site.index'),

View file

@ -57,7 +57,7 @@ def check_location(location: Optional[str], request) -> Optional[SpecificLocatio
return location return location
def map_index(request, mode=None, slug=None, slug2=None, details=None, options=None, def map_index(request, mode=None, slug=None, slug2=None, details=None, options=None, nearby=None,
level=None, x=None, y=None, zoom=None, embed=None): level=None, x=None, y=None, zoom=None, embed=None):
# check for access token # check for access token
@ -108,6 +108,7 @@ def map_index(request, mode=None, slug=None, slug2=None, details=None, options=N
'sidebar': routing or destination is not None, 'sidebar': routing or destination is not None,
'details': True if details else False, 'details': True if details else False,
'options': True if options else False, 'options': True if options else False,
'nearby': True if nearby else False,
} }
levels = levels_by_short_label_for_request(request) levels = levels_by_short_label_for_request(request)

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB