forms: sync map with address fields (#27418)

This commit is contained in:
Frédéric Péters 2019-04-13 18:06:02 +02:00
parent adb5b170ac
commit 1f0bea71a2
6 changed files with 73 additions and 0 deletions

View File

@ -2458,3 +2458,24 @@ def test_formdef_submit_structured(pub, local_user):
}
data_class.wipe()
def test_geocoding(pub):
with mock.patch('qommon.misc.urlopen') as urlopen:
urlopen.side_effect = lambda *args: StringIO(json.dumps([{'lat': 0, 'lon': 0}]))
get_app(pub).get('/api/geocoding', status=400)
resp = get_app(pub).get('/api/geocoding?q=test')
assert resp.content_type == 'application/json'
assert resp.body == json.dumps([{'lat': 0, 'lon': 0}])
assert urlopen.call_args[0][0] == 'http://nominatim.openstreetmap.org/search?format=json&q=test&accept-language=en'
pub.site_options.add_section('options')
pub.site_options.set('options', 'nominatim_key', 'KEY')
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
resp = get_app(pub).get('/api/geocoding?q=test')
assert urlopen.call_args[0][0] == 'http://nominatim.openstreetmap.org/search?key=KEY&format=json&q=test&accept-language=en'
pub.site_options.set('options', 'geocoding_service_url', 'http://reverse.example.net/?param=value')
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
resp = get_app(pub).get('/api/geocoding?q=test')
assert urlopen.call_args[0][0] == 'http://reverse.example.net/?param=value&format=json&q=test&accept-language=en'

View File

@ -817,6 +817,20 @@ def reverse_geocoding(request, *args, **kwargs):
url += '&accept-language=%s' % (get_publisher().get_site_language() or 'en')
return HttpResponse(misc.urlopen(url).read(), content_type='application/json')
def geocoding(request, *args, **kwargs):
if not 'q' in request.GET:
return HttpResponseBadRequest()
q = request.GET['q']
url = get_publisher().get_geocoding_service_url()
if '?' in url:
url += '&'
else:
url += '?'
url += 'format=json&q=%s' % urllib.quote(q.encode('utf-8'))
url += '&accept-language=%s' % (get_publisher().get_site_language() or 'en')
return HttpResponse(misc.urlopen(url).read(), content_type='application/json')
def validate_expression(request, *args, **kwargs):
expression = request.GET.get('expression')
hint = {'klass': None, 'msg': ''}

View File

@ -2108,6 +2108,7 @@ class MapWidget(CompositeWidget):
self.readonly = kwargs.pop('readonly', False)
self.map_attributes = {}
self.map_attributes.update(get_publisher().get_map_attributes())
self.sync_map_and_address_fields = get_publisher().has_site_option('sync-map-and-address-fields')
for attribute in ('initial_zoom', 'min_zoom', 'max_zoom', 'init_with_geoloc'):
if attribute in kwargs:
self.map_attributes['data-' + attribute] = kwargs.pop(attribute)

View File

@ -26,6 +26,7 @@ function geoloc_prefill(element_type, element_value)
$(document).on('set-geolocation', function(event, coords) {
$.getJSON(WCS_ROOT_URL + '/api/reverse-geocoding?lat=' + coords.lat + '&lon=' + coords.lng, function(data) {
unset_sync_callback()
geoloc_prefill('house', data.address.house_number);
var street = data.address.road;
if (!street && data.address.pedestrian) {
@ -41,6 +42,7 @@ function geoloc_prefill(element_type, element_value)
geoloc_prefill('postcode', data.address.postcode);
geoloc_prefill('city', data.address.village || data.address.town || data.address.city || data.address.county);
geoloc_prefill('country', data.address.country);
set_sync_callback()
});
});
if ($('.qommon-map').length == 0) {
@ -62,4 +64,37 @@ function geoloc_prefill(element_type, element_value)
);
}
}
function set_sync_callback() {
var $map = $('.qommon-map');
if (! $map.data('address-sync')) return;
$('div[data-geolocation]').on('change', 'input[type=text], textarea, select', function(event) {
var address = '';
$(['number-and-street', 'house', 'road', 'postcode', 'city', 'country']).each(function(idx, elem) {
var part = $('div[data-geolocation="' + elem + '"]').find('input, textarea, select').val();
if (part) {
address += part + ' ';
if (elem == 'number-and-street' || elem == 'road' || elem == 'city') {
address += ', ';
}
}
});
$.getJSON(WCS_ROOT_URL + '/api/geocoding?q=' + address, function(data) {
if (data && $(data).length > 0) {
var coords = {lat: data[0].lat, lon: data[0].lon};
var map = $map[0].leaflet_map;
map.flyTo(coords);
if (map.marker === null) {
map.marker = L.marker([0, 0]);
map.marker.addTo(map);
}
map.marker.setLatLng(coords);
}
});
});
}
function unset_sync_callback() {
$('div[data-geolocation]').off('change', 'input[type=text], textarea, select');
}
set_sync_callback();
});

View File

@ -4,6 +4,7 @@
<input type="hidden" name="{{widget.name}}$latlng" {% if widget.value %}value="{{widget.value}}"{% endif %}>
<div id="map-{{widget.name}}" class="qommon-map"
{% if widget.readonly %}data-readonly="true"{% endif %}
{% if widget.sync_map_and_address_fields %}data-address-sync="true"{% endif %}
{% for key, value in widget.map_attributes.items %}{{key}}="{{value}}" {% endfor %}
{% if widget.initial_position %}
data-init-lat="{{ widget.initial_position.lat }}"

View File

@ -27,6 +27,7 @@ urlpatterns = [
url(r'^api/validate-condition$', api.validate_condition, name='api-validate-condition'),
url(r'^api/validate-expression$', api.validate_expression, name='api-validate-expression'),
url(r'^api/reverse-geocoding$', api.reverse_geocoding, name='api-reverse-geocoding'),
url(r'^api/geocoding$', api.geocoding, name='api-geocoding'),
# provide django.contrib.auth view names for compatibility with
# templates created for classic django applications.