select location from map
This commit is contained in:
parent
d9632dc0c4
commit
0b44acc8b0
7 changed files with 163 additions and 31 deletions
|
@ -292,7 +292,7 @@ class PointLocation(Location):
|
||||||
def subtitle(self) -> str:
|
def subtitle(self) -> str:
|
||||||
return 'Coordinates'
|
return 'Coordinates'
|
||||||
|
|
||||||
def to_location_json(self):
|
def to_json(self):
|
||||||
result = super().to_location_json()
|
result = super().to_location_json()
|
||||||
result['level'] = self.level.name
|
result['level'] = self.level.name
|
||||||
result['x'] = self.x
|
result['x'] = self.x
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from calendar import timegm
|
from calendar import timegm
|
||||||
|
from collections import OrderedDict
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
@ -90,7 +91,7 @@ class CachedReadOnlyViewSetMixin():
|
||||||
@cache_result('c3nav__mapdata__levels')
|
@cache_result('c3nav__mapdata__levels')
|
||||||
def get_levels_cached():
|
def get_levels_cached():
|
||||||
from c3nav.mapdata.models import Level
|
from c3nav.mapdata.models import Level
|
||||||
return {level.name: level for level in Level.objects.all()}
|
return OrderedDict((level.name, level) for level in Level.objects.all())
|
||||||
|
|
||||||
|
|
||||||
@cache_result('c3nav__mapdata__packages')
|
@cache_result('c3nav__mapdata__packages')
|
||||||
|
|
|
@ -44,6 +44,9 @@ body, .btn {
|
||||||
.locationselect .icons .link {
|
.locationselect .icons .link {
|
||||||
background-image:url('../img/icons/link.svg');
|
background-image:url('../img/icons/link.svg');
|
||||||
}
|
}
|
||||||
|
.locationselect .icons .map {
|
||||||
|
background-image:url('../img/icons/map.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.location-group .only-if-selected {
|
.location-group .only-if-selected {
|
||||||
display:none;
|
display:none;
|
||||||
|
@ -52,6 +55,39 @@ body, .btn {
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.locationselect-map {
|
||||||
|
display:none;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
.location-group.map .locationselect-map {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.location-group.map .locationselect-input {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.locationselect-map .map-container {
|
||||||
|
width:100%;
|
||||||
|
overflow:auto;
|
||||||
|
height:200px;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
.locationselect-map .map-container img, .locationselect-map .map-container input {
|
||||||
|
position:absolute;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
}
|
||||||
|
.locationselect-map .map-buttons {
|
||||||
|
position:absolute;
|
||||||
|
top:8px;
|
||||||
|
left:8px;
|
||||||
|
}
|
||||||
|
.locationselect-map .scroll-hint {
|
||||||
|
color:#FFFFFF;
|
||||||
|
position:absolute;
|
||||||
|
top:150px;
|
||||||
|
left:16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.location {
|
.location {
|
||||||
font-size:18px;
|
font-size:18px;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
c3nav = {
|
c3nav = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (!$('.main-view').length) return;
|
c3nav.main_view = $('.main-view');
|
||||||
|
if (!c3nav.main_view.length) return;
|
||||||
|
|
||||||
|
c3nav.svg_width = parseInt(c3nav.main_view.attr('data-svg-width'));
|
||||||
|
c3nav.svg_height = parseInt(c3nav.main_view.attr('data-svg-height'));
|
||||||
|
|
||||||
c3nav._typeahead_locations = new Bloodhound({
|
c3nav._typeahead_locations = new Bloodhound({
|
||||||
datumTokenizer: function(data) {
|
datumTokenizer: function(data) {
|
||||||
|
@ -33,6 +37,10 @@ c3nav = {
|
||||||
c3nav.locationselect_focus();
|
c3nav.locationselect_focus();
|
||||||
|
|
||||||
$('.locationselect .icons .reset').click(c3nav._locationselect_reset);
|
$('.locationselect .icons .reset').click(c3nav._locationselect_reset);
|
||||||
|
$('.locationselect .icons .map').click(c3nav._locationselect_activate_map);
|
||||||
|
$('.locationselect .close-map').click(c3nav._locationselect_close_map);
|
||||||
|
$('.locationselect .level-selector a').click(c3nav._locationselect_click_level);
|
||||||
|
$('.locationselect .map-container').on('click', 'img', c3nav._locationselect_click_image);
|
||||||
$('#route-from-here').click(c3nav._click_route_from_here);
|
$('#route-from-here').click(c3nav._click_route_from_here);
|
||||||
$('#route-to-here').click(c3nav._click_route_to_here);
|
$('#route-to-here').click(c3nav._click_route_to_here);
|
||||||
|
|
||||||
|
@ -47,6 +55,49 @@ c3nav = {
|
||||||
location_group.find('.tt-suggestion').remove();
|
location_group.find('.tt-suggestion').remove();
|
||||||
c3nav._locations_changed();
|
c3nav._locations_changed();
|
||||||
},
|
},
|
||||||
|
_locationselect_activate_map: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var location_group = $(this).closest('.location-group');
|
||||||
|
location_group.addClass('map');
|
||||||
|
var map_container = location_group.find('.map-container');
|
||||||
|
console.log(c3nav.svg_height-(map_container.height()/2));
|
||||||
|
map_container.scrollTop((c3nav.svg_height-map_container.height())/2).scrollLeft((c3nav.svg_width-map_container.width())/2);
|
||||||
|
location_group.find('.level-selector a').first().click();
|
||||||
|
},
|
||||||
|
_locationselect_close_map: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var location_group = $(this).closest('.location-group');
|
||||||
|
location_group.removeClass('map').find('.tt-input').focus();
|
||||||
|
},
|
||||||
|
_locationselect_click_level: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var location_group = $(this).closest('.location-group');
|
||||||
|
var map_container = location_group.find('.map-container');
|
||||||
|
var level = $(this).attr('data-level');
|
||||||
|
map_container.find('img').remove();
|
||||||
|
map_container.append($('<img>').attr({
|
||||||
|
'src': '/map/'+level+'.png',
|
||||||
|
'width': c3nav.svg_width,
|
||||||
|
'height': c3nav.svg_height
|
||||||
|
}));
|
||||||
|
map_container.attr('data-level', level);
|
||||||
|
},
|
||||||
|
_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 location_group = $(this).closest('.location-group');
|
||||||
|
location_group.removeClass('map').addClass('selected');
|
||||||
|
var selected = location_group.find('.locationselect-selected');
|
||||||
|
selected.find('.title').text('');
|
||||||
|
selected.find('.subtitle').text('');
|
||||||
|
selected.find('.id-field').val(coords);
|
||||||
|
$.getJSON('/api/locations/'+coords, function(data) {
|
||||||
|
selected.find('.title').text(data.title);
|
||||||
|
selected.find('.subtitle').text(data.subtitle);
|
||||||
|
});
|
||||||
|
c3nav._locations_changed();
|
||||||
|
c3nav.locationselect_focus();
|
||||||
|
},
|
||||||
locationselect_focus: function() {
|
locationselect_focus: function() {
|
||||||
$('.location-group:visible:not(.selected) .locationselect-input .tt-input').first().focus();
|
$('.location-group:visible:not(.selected) .locationselect-input .tt-input').first().focus();
|
||||||
},
|
},
|
||||||
|
@ -59,7 +110,7 @@ c3nav = {
|
||||||
},
|
},
|
||||||
_click_route_x_here: function(e, location_group) {
|
_click_route_x_here: function(e, location_group) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('.main-view').removeClass('mode-location').addClass('mode-route');
|
c3nav.main_view.removeClass('mode-location').addClass('mode-route');
|
||||||
from_group = $('.location-select');
|
from_group = $('.location-select');
|
||||||
from_group.removeClass('selected');
|
from_group.removeClass('selected');
|
||||||
location_group.addClass('selected').find('.id-field').val(from_group.find('.id-field').val());
|
location_group.addClass('selected').find('.id-field').val(from_group.find('.id-field').val());
|
||||||
|
@ -107,8 +158,9 @@ c3nav = {
|
||||||
|
|
||||||
_locations_changed: function(e) {
|
_locations_changed: function(e) {
|
||||||
var url;
|
var url;
|
||||||
if ($('.main-view').is('.mode-location')) {
|
if (c3nav.main_view.is('.mode-location')) {
|
||||||
url = '/l/'+$(':input[name=location]').val()+'/';
|
var location = $(':input[name=location]').val()
|
||||||
|
url = (location !== '') ? '/l/'+location+'/' : '/';
|
||||||
} else {
|
} else {
|
||||||
var origin = $(':input[name=origin]').val();
|
var origin = $(':input[name=origin]').val();
|
||||||
var destination = $(':input[name=destination]').val();
|
var destination = $(':input[name=destination]').val();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="col-md-{% if name == 'location' %}12{% else %}6{% endif %} location-group {{ name }}-select{% if location %} selected{% endif %}">
|
<div class="col-md-{% if name == 'location' %}12{% else %}6{% endif %} location-group {{ name }}-select{% if location %} selected{% endif %}{% if active_field == name and map_level %} map{% endif %}">
|
||||||
<div class="form-group{% if search == name and not search_results %} has-error{% endif %}" data-name="{{ name }}">
|
<div class="form-group{% if search == name and not search_results %} has-error{% endif %}" data-name="{{ name }}">
|
||||||
<label for="{{ name }}_input">{{ heading }}</label>
|
<label for="{{ name }}_input">{{ heading }}</label>
|
||||||
<div class="locationselect">
|
<div class="locationselect">
|
||||||
|
@ -23,6 +23,27 @@
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="icons">
|
||||||
|
<a href="?map-level={{ levels.0 }}" class="map"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="locationselect-map">
|
||||||
|
<div class="map-container">
|
||||||
|
<div class="dummy" style="width:{{ svg_width }}px;height:{{ svg_height }}px;"></div>
|
||||||
|
{% if map_level %}
|
||||||
|
<input type="image" src="/map/{{ map_level }}.png" width="{{ svg_width }}" height="{{ svg_height }}">
|
||||||
|
<p class="scroll-hint">{% trans 'You have Javascript deactivated. Please scroll in this direction ⇘' %}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="map-buttons">
|
||||||
|
<div class="btn-group level-selector" role="group">
|
||||||
|
{% for level in levels %}
|
||||||
|
<a href="?map-level={{ level }}" data-level="{{ level }}" class="btn btn-default{% if level == map_level %} active{% endif %}">{{ level }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<a href="?" class="btn btn-default close-map">{% trans 'close' %}</a>
|
||||||
|
</div>
|
||||||
|
<span class="help-block">{% trans 'Click or tap the desired location on the map.' %}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="locationselect-selected">
|
<div class="locationselect-selected">
|
||||||
<div class="location form-control input-lg">
|
<div class="location form-control input-lg">
|
||||||
|
@ -52,7 +73,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not location %}
|
{% if not location and not map_level %}
|
||||||
<noscript>
|
<noscript>
|
||||||
<button type="submit" class="btn btn-primary btn-block">{% trans 'Search' %}</button>
|
<button type="submit" class="btn btn-primary btn-block">{% trans 'Search' %}</button>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form method="post" class="main-view mode-{{ mode }}{% if origin and destination %} can-route{% endif %}">
|
<form method="post" class="main-view mode-{{ mode }}{% if origin and destination %} can-route{% endif %}" data-svg-width="{{ svg_width }}" data-svg-height="{{ svg_height }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row locations">
|
<div class="row locations">
|
||||||
{% trans "Location" as heading %}
|
{% trans "Location" as heading %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ from c3nav.mapdata.models import Level
|
||||||
from c3nav.mapdata.models.locations import get_location, search_location
|
from c3nav.mapdata.models.locations import get_location, search_location
|
||||||
from c3nav.mapdata.permissions import get_excludables_includables
|
from c3nav.mapdata.permissions import get_excludables_includables
|
||||||
from c3nav.mapdata.render.compose import composer
|
from c3nav.mapdata.render.compose import composer
|
||||||
|
from c3nav.mapdata.utils.cache import get_levels_cached
|
||||||
from c3nav.mapdata.utils.misc import get_dimensions
|
from c3nav.mapdata.utils.misc import get_dimensions
|
||||||
from c3nav.routing.graph import Graph
|
from c3nav.routing.graph import Graph
|
||||||
|
|
||||||
|
@ -49,38 +50,68 @@ def main(request, location=None, origin=None, destination=None):
|
||||||
|
|
||||||
mode = 'location' if not origin and not destination else 'route'
|
mode = 'location' if not origin and not destination else 'route'
|
||||||
|
|
||||||
|
active_field = None
|
||||||
|
if not origin and not destination:
|
||||||
|
active_field = 'location'
|
||||||
|
elif origin and not destination:
|
||||||
|
active_field = 'destination'
|
||||||
|
elif destination and not origin:
|
||||||
|
active_field = 'origin'
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'location': location,
|
'location': location,
|
||||||
'origin': origin,
|
'origin': origin,
|
||||||
'destination': destination,
|
'destination': destination,
|
||||||
'mode': mode,
|
'mode': mode,
|
||||||
|
'active_field': active_field,
|
||||||
}
|
}
|
||||||
|
|
||||||
search = None
|
width, height = get_dimensions()
|
||||||
if not origin and not destination:
|
levels = tuple(name for name, level in get_levels_cached().items() if not level.intermediate)
|
||||||
search = 'location'
|
|
||||||
elif origin and not destination:
|
|
||||||
search = 'destination'
|
|
||||||
elif destination and not origin:
|
|
||||||
search = 'origin'
|
|
||||||
|
|
||||||
if search is not None:
|
ctx.update({
|
||||||
search_query = request.POST.get(search+'_search', '').strip() or None
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'svg_width': int(width * 6),
|
||||||
|
'svg_height': int(height * 6),
|
||||||
|
'levels': levels,
|
||||||
|
})
|
||||||
|
|
||||||
|
map_level = request.GET.get('map-level')
|
||||||
|
if map_level in levels:
|
||||||
|
ctx.update({
|
||||||
|
'map_level': map_level
|
||||||
|
})
|
||||||
|
|
||||||
|
if 'x' in request.POST and 'y' in request.POST:
|
||||||
|
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))
|
||||||
|
if active_field == 'origin':
|
||||||
|
return redirect('site.route', origin=coords, destination=destination.location_id)
|
||||||
|
elif active_field == 'destination':
|
||||||
|
return redirect('site.route', origin=origin.location_id, destination=coords)
|
||||||
|
elif active_field == 'location':
|
||||||
|
return redirect('site.location', location=coords)
|
||||||
|
|
||||||
|
if active_field is not None:
|
||||||
|
search_query = request.POST.get(active_field+'_search', '').strip() or None
|
||||||
if search_query:
|
if search_query:
|
||||||
results = search_location(request, search_query)
|
results = search_location(request, search_query)
|
||||||
|
|
||||||
url = 'site.location' if search == 'location' else 'site.route'
|
url = 'site.location' if active_field == 'location' else 'site.route'
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if origin:
|
if origin:
|
||||||
kwargs['origin'] = origin.location_id
|
kwargs['origin'] = origin.location_id
|
||||||
if destination:
|
if destination:
|
||||||
kwargs['destination'] = destination.location_id
|
kwargs['destination'] = destination.location_id
|
||||||
for result in results:
|
for result in results:
|
||||||
kwargs[search] = result.location_id
|
kwargs[active_field] = result.location_id
|
||||||
result.url = reverse(url, kwargs=kwargs)
|
result.url = reverse(url, kwargs=kwargs)
|
||||||
|
|
||||||
ctx.update({
|
ctx.update({
|
||||||
'search': search,
|
'search': active_field,
|
||||||
'search_query': search_query,
|
'search_query': search_query,
|
||||||
'search_results': results,
|
'search_results': results,
|
||||||
})
|
})
|
||||||
|
@ -159,15 +190,6 @@ def main(request, location=None, origin=None, destination=None):
|
||||||
'route': route,
|
'route': route,
|
||||||
})
|
})
|
||||||
|
|
||||||
width, height = get_dimensions()
|
|
||||||
|
|
||||||
ctx.update({
|
|
||||||
'width': width,
|
|
||||||
'height': height,
|
|
||||||
'svg_width': width*6,
|
|
||||||
'svg_height': height*6,
|
|
||||||
})
|
|
||||||
|
|
||||||
response = render(request, 'site/main.html', ctx)
|
response = render(request, 'site/main.html', ctx)
|
||||||
|
|
||||||
if request.method == 'POST' and save_settings:
|
if request.method == 'POST' and save_settings:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue