From 36df3038567190f4e329780b5618439ce84de4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Fri, 10 Oct 2014 19:19:25 +0200 Subject: [PATCH] map field (#5668) --- wcs/fields.py | 51 ++++++++++++++++++++++++++++++++++++++ wcs/qommon/form.py | 35 ++++++++++++++++++++++++++ wcs/qommon/http_response.py | 4 +++ wcs/qommon/static/css/qommon.css | 4 +++ wcs/qommon/static/js/qommon.map.js | 51 ++++++++++++++++++++++++++++++++++++++ wcs/root.py | 4 +++ 6 files changed, 149 insertions(+) create mode 100644 wcs/qommon/static/js/qommon.map.js diff --git a/wcs/fields.py b/wcs/fields.py index c346203..a869ac2 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -15,6 +15,7 @@ # along with this program; if not, see . import time +import random import re import xml.etree.ElementTree as ET @@ -1348,6 +1349,56 @@ class TableRowsField(WidgetField): register_field_class(TableRowsField) +class MapField(WidgetField): + key = 'map' + description = N_('Map') + + initial_zoom = None + min_zoom = None + max_zoom = None + default_position = None + init_with_geoloc = False + + widget_class = MapWidget + extra_attributes = ['initial_zoom', 'min_zoom', 'max_zoom', + 'default_position', 'init_with_geoloc'] + + def fill_admin_form(self, form): + WidgetField.fill_admin_form(self, form) + # 0: whole world, 9: wide area, 11: area, 13: town, 16: small road + zoom_levels = [(None, '---'), + ('0', _('Whole world')), + ('9', _('Wide area')), + ('11', _('Area')), + ('13', _('Town')), + ('16', _('Small road'))] + form.add(SingleSelectWidget, 'initial_zoom', title=_('Initial zoom level'), + value=self.initial_zoom or '13', options=zoom_levels) + form.add(SingleSelectWidget, 'min_zoom', title=_('Minimal zoom level'), + value=self.min_zoom, options=zoom_levels, required=False) + form.add(SingleSelectWidget, 'max_zoom', title=_('Maximal zoom level'), + value=self.max_zoom, options=zoom_levels, required=False) + form.add(MapWidget, 'default_position', title=_('Initial Position'), + value=self.default_position, default_zoom='9', required=False) + form.add(CheckboxWidget, 'init_with_geoloc', + title=_('Initialize position using device geolocation'), + value=self.init_with_geoloc, required=False) + + def get_admin_attributes(self): + return WidgetField.get_admin_attributes(self) + ['initial_zoom', + 'min_zoom', 'max_zoom', 'default_position', + 'init_with_geoloc'] + + def get_view_value(self, value): + widget = self.widget_class('x%s' % random.random(), value, readonly=True) + return widget.render_content() + + def get_rst_view_value(self, value, indent=''): + return indent + value + +register_field_class(MapField) + + class RankedItemsField(WidgetField): key = 'ranked-items' description = N_('Ranked Items') diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py index b0c4e3f..9e573a6 100644 --- a/wcs/qommon/form.py +++ b/wcs/qommon/form.py @@ -1747,3 +1747,38 @@ class PasswordEntryWidget(CompositeWidget): self.value = hashlib.sha1(pwd1).hexdigest() else: self.value = None + + +class MapWidget(CompositeWidget): + def __init__(self, name, value=None, **kwargs): + CompositeWidget.__init__(self, name, value, **kwargs) + self.add(HiddenWidget, 'latlng') + self.readonly = kwargs.pop('readonly', False) + self.kwargs = kwargs + + def render_content(self): + get_response().add_javascript(['qommon.map.js']) + r = TemplateIO(html=True) + for widget in self.get_widgets(): + r += widget.render() + attrs = { + 'class': 'qommon-map', + 'id': 'map-%s' % self.name, + } + if self.value: + attrs['data-init-lat'], attrs['data-init-lng'] = self.value.split(';') + if self.readonly: + attrs['data-readonly'] = 'true' + for attribute in ('initial_zoom', 'min_zoom', 'max_zoom'): + if attribute in self.kwargs: + attrs['data-%s' % attribute] = self.kwargs.get(attribute) + if self.kwargs.get('default_position'): + attrs['data-def-lat'], attrs['data-def-lng'] = self.kwargs.get('default_position').split(';') + if self.kwargs.get('init_with_geoloc'): + attrs['data-init-with-geologc'] = 1 + r += htmltext('
' % ' '.join(['%s="%s"' % x for x in attrs.items()])) + return r.getvalue() + + def _parse(self, request): + CompositeWidget._parse(self, request) + self.value = self.get('latlng') diff --git a/wcs/qommon/http_response.py b/wcs/qommon/http_response.py index 6d8e2ab..a13bcb9 100644 --- a/wcs/qommon/http_response.py +++ b/wcs/qommon/http_response.py @@ -61,6 +61,10 @@ class HTTPResponse(quixote.http_response.HTTPResponse): self.javascript_scripts = [] for script_name in script_names: if not script_name in self.javascript_scripts: + if script_name == 'qommon.map.js': + self.add_javascript(['jquery.js']) + self.add_javascript(['../../leaflet/leaflet.js']) + self.add_css_include('../../leaflet/leaflet.css') self.javascript_scripts.append(str(script_name)) if script_name == 'afterjob.js': self.add_javascript_code('var QOMMON_ROOT_URL = "%s";\n' % \ diff --git a/wcs/qommon/static/css/qommon.css b/wcs/qommon/static/css/qommon.css index a753050..c6373e0 100644 --- a/wcs/qommon/static/css/qommon.css +++ b/wcs/qommon/static/css/qommon.css @@ -389,3 +389,7 @@ ul.select2-results { outline: 0; border: 1px solid #aaa; } + +div.qommon-map { + height: 280px; +} diff --git a/wcs/qommon/static/js/qommon.map.js b/wcs/qommon/static/js/qommon.map.js new file mode 100644 index 0000000..aa2e914 --- /dev/null +++ b/wcs/qommon/static/js/qommon.map.js @@ -0,0 +1,51 @@ +$(function() { + $('.qommon-map').each(function() { + var map_options = Object(); + var initial_zoom = parseInt($(this).data('initial_zoom')); + if (! isNaN(initial_zoom)) { + map_options.zoom = initial_zoom; + } else { + map_options.zoom = 13; + } + var max_zoom = parseInt($(this).data('max_zoom')); + if (! isNaN(max_zoom)) map_options.maxZoom = max_zoom; + var min_zoom = parseInt($(this).data('min_zoom')); + if (! isNaN(min_zoom)) map_options.minZoom = min_zoom; + var map = L.map($(this).attr('id'), map_options); + var hidden = $(this).prev(); + map.marker = null; + var latlng; + if ($(this).data('init-lat')) { + latlng = [$(this).data('init-lat'), $(this).data('init-lng')] + map.marker = L.marker(latlng); + map.marker.addTo(map); + } else if ($(this).data('def-lat')) { + latlng = [$(this).data('def-lat'), $(this).data('def-lng')] + } else { + latlng = [50.84, 4.36]; + } + map.setView(latlng, map_options.zoom); + L.tileLayer( + 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + { + attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA' + }).addTo(map); + if (! $(this).data('readonly')) { + map.on('click', function(e) { + if (map.marker === null) { + map.marker = L.marker([0, 0]); + map.marker.addTo(map); + } + map.marker.setLatLng(e.latlng); + hidden.val(e.latlng.lat + ';' + e.latlng.lng); + }); + } + if ($(this).data('init-with-geoloc')) { + map.on('locationfound', function(e) { + hidden.val(e.latlng.lat + ';' + e.latlng.lng); + map.setView(e.latlng, map_options.zoom); + }); + map.locate({timeout: 1000, maximumAge: 60000}); + } + }); +}); diff --git a/wcs/root.py b/wcs/root.py index dcb8d94..4400f4b 100644 --- a/wcs/root.py +++ b/wcs/root.py @@ -359,6 +359,10 @@ class RootDirectory(Directory): dirname = os.path.join(get_publisher().data_dir, 'qommon') return StaticDirectory(dirname, follow_symlinks = True) + # maps /leaflet/ to the directory provided by the libjs-openlayers package + if component == 'leaflet': + return StaticDirectory('/usr/share/javascript/leaflet') + # is this a category ? try: category = Category.get_by_urlname(component) -- 2.1.1