summaryrefslogtreecommitdiff
path: root/chimere/widgets.py
diff options
context:
space:
mode:
Diffstat (limited to 'chimere/widgets.py')
-rw-r--r--chimere/widgets.py901
1 files changed, 901 insertions, 0 deletions
diff --git a/chimere/widgets.py b/chimere/widgets.py
new file mode 100644
index 0000000..6d61965
--- /dev/null
+++ b/chimere/widgets.py
@@ -0,0 +1,901 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2008-2016 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+"""
+Extra widgets and fields
+"""
+
+from json import JSONEncoder
+
+from django import forms
+from django.conf import settings
+from django.contrib.gis.db import models
+from django.contrib.gis.geos import fromstr
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.urlresolvers import reverse
+from django.forms.widgets import RadioInput, RadioFieldRenderer, flatatt
+from django.utils.encoding import force_unicode, smart_unicode
+from django.utils.html import conditional_escape, escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _
+from django.template.loader import render_to_string
+
+import re
+
+
+def getMapJS(area_name=''):
+ '''Variable initialization for drawing the map
+ '''
+ # projection, center and bounds definitions
+ js = u"var epsg_display_projection = 'EPSG:%d';\n" \
+ % settings.CHIMERE_EPSG_DISPLAY_PROJECTION
+ js += u"var epsg_projection = 'EPSG:%d';\n" % \
+ settings.CHIMERE_EPSG_PROJECTION
+ js += u"var centerLonLat = ol.proj.transform("\
+ u"[%f, %f], epsg_display_projection, epsg_projection);\n" % \
+ settings.CHIMERE_DEFAULT_CENTER
+ js += u"var media_path = '%s';\n" % settings.MEDIA_URL
+ js += u"var static_path = '%s';\n" % settings.STATIC_URL
+ js += u"var map_layer = %s;\n" % settings.CHIMERE_DEFAULT_MAP_LAYER
+ js += u"var restricted_extent;\n"
+
+ if area_name:
+ js += u"var area_name='%s';\n" % area_name
+ js = u"<script type='text/javascript'><!--\n"\
+ u"%s// !--></script>\n" % js
+ return js
+
+
+def get_map_layers(area_name='', get_area_zoom=False):
+ from chimere.models import Area
+ area = None
+ if area_name:
+ try:
+ area = Area.objects.get(urn=area_name)
+ except ObjectDoesNotExist:
+ pass
+ else:
+ try:
+ area = Area.objects.get(default=True)
+ except ObjectDoesNotExist:
+ pass
+ map_layers, default = [], None
+ if area and area.layers.count():
+ map_layers = [
+ [layer.name, layer.layer_code, False, layer.extra_js_code or '']
+ for layer in area.layers.order_by('arealayers__order').all()]
+ def_layer = area.layers.filter(arealayers__default=True)
+ if def_layer.count():
+ def_layer = def_layer.all()[0]
+ for order, map_layer in enumerate(map_layers):
+ if map_layer[1] == def_layer.layer_code:
+ default = order
+ map_layers[order][2] = True
+ else:
+ map_layers[0][2] = True
+ elif settings.CHIMERE_DEFAULT_MAP_LAYER:
+ map_layers = [(_(u"Default layer"), settings.CHIMERE_DEFAULT_MAP_LAYER,
+ True, '')]
+ else:
+ map_layers = [(u"OSM", """new ol.layer.Tile({
+ style: 'Road',
+ source: new ol.source.OSM()
+})""", True, '')]
+ if not get_area_zoom:
+ return map_layers, default
+ if not area:
+ return map_layers, default, settings.CHIMERE_DEFAULT_ZOOM
+ zoom = "[%s]" % ",".join(area.getExtent())
+ return map_layers, default, zoom
+
+
+class ChosenSelectWidget(forms.Select):
+ """
+ Chosen select widget.
+ """
+ class Media:
+ js = ["%schosen/chosen/chosen.jquery.min.js" % settings.STATIC_URL]
+ css = {
+ 'all':
+ ["%schosen/chosen/chosen.css" % settings.STATIC_URL]
+ }
+
+ def render(self, *args, **kwargs):
+ if 'attrs' not in kwargs:
+ kwargs['attrs'] = {}
+ kwargs['attrs'].update({'class': 'chzn-select'})
+ rendered = super(ChosenSelectWidget, self).render(*args, **kwargs)
+ rendered += u"\n<script type='text/javascript'>\n"\
+ u" $('#%s').chosen();\n"\
+ u"</script>\n" % kwargs['attrs']['id']
+ return mark_safe(rendered)
+
+"""
+JQuery UI button select widget.
+"""
+
+
+class ButtonRadioInput(RadioInput):
+ def render(self, name=None, value=None, attrs=None, choices=()):
+ name = name or self.name
+ value = value or self.value
+ attrs = attrs or self.attrs
+ if 'id' in self.attrs:
+ label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
+ else:
+ label_for = ''
+ choice_label = conditional_escape(force_unicode(self.choice_label))
+ return mark_safe(u'%s <label%s>%s</label>' % (self.tag(), label_for,
+ choice_label))
+
+
+class ButtonRadioFieldRenderer(RadioFieldRenderer):
+ def __iter__(self):
+ for i, choice in enumerate(self.choices):
+ yield ButtonRadioInput(self.name, self.value, self.attrs.copy(),
+ choice, i)
+
+ def render(self):
+ return mark_safe(u'\n'.join([force_unicode(w) for w in self]))
+
+
+class ButtonSelectWidget(forms.RadioSelect):
+ def __init__(self, *args, **kwargs):
+ self.renderer = ButtonRadioFieldRenderer
+ super(ButtonSelectWidget, self).__init__(*args, **kwargs)
+
+ def render(self, *args, **kwargs):
+ rendered = "<div id='%s'>\n" % kwargs['attrs']['id']
+ rendered += super(ButtonSelectWidget, self).render(*args, **kwargs)
+ rendered += u"\n<script type='text/javascript'>\n"\
+ u" $('#%s').buttonset();\n"\
+ u"</script>\n</div>\n" % kwargs['attrs']['id']
+ return mark_safe(rendered)
+
+
+class ImporterChoicesWidget(forms.Select):
+ '''
+ Importer select widget.
+ '''
+ class Media:
+ js = ["%schimere/js/importer_interface.js" % settings.STATIC_URL]
+
+TINYMCE_JS, FULL_TINY_JS, ADMIN_TINY_JS = [], [], []
+
+if settings.TINYMCE_URL:
+ TINYMCE_JS = ["%stiny_mce.js" % settings.TINYMCE_URL]
+ FULL_TINY_JS = TINYMCE_JS[:] + \
+ ["%schimere/js/textareas.js" % settings.STATIC_URL]
+ ADMIN_TINY_JS = TINYMCE_JS[:] + \
+ ["%schimere/js/textareas_admin.js" % settings.STATIC_URL]
+
+
+class TextareaWidgetBase(forms.Textarea):
+ """
+ Manage the edition of a text using TinyMCE
+ """
+ def render(self, *args, **kwargs):
+ if not TINYMCE_JS:
+ rendered = super(TextareaWidgetBase, self).render(*args, **kwargs)
+ return mark_safe(rendered)
+ if 'attrs' not in kwargs:
+ kwargs['attrs'] = {}
+ if 'class' not in kwargs['attrs']:
+ kwargs['attrs']['class'] = ''
+ else:
+ kwargs['attrs']['class'] += ' '
+ kwargs['attrs']['class'] += 'mceEditor'
+ rendered = super(TextareaWidgetBase, self).render(*args, **kwargs)
+ return mark_safe(rendered)
+
+
+class FullTextareaWidget(TextareaWidgetBase):
+ """
+ Manage the edition of a text using TinyMCE
+ """
+ class Media:
+ js = TINYMCE_JS
+
+ def render(self, *args, **kwargs):
+ if not TINYMCE_JS:
+ rendered = super(FullTextareaWidget, self).render(*args, **kwargs)
+ return mark_safe(rendered)
+
+ if 'attrs' not in kwargs:
+ kwargs['attrs'] = {}
+ if 'class' not in kwargs['attrs']:
+ kwargs['attrs']['class'] = ''
+ else:
+ kwargs['attrs']['class'] += ' '
+ kwargs['attrs']['class'] += 'mceEditor'
+ rendered = super(FullTextareaWidget, self).render(*args, **kwargs)
+ rendered += u"""<script type='text/javascript'>
+tinyMCE.init({
+ mode : "textareas",
+ theme : "advanced",
+ relative_urls : false,
+ editor_selector : "mceEditor"
+});
+</script>
+"""
+ return mark_safe(rendered)
+
+
+class TextareaWidget(TextareaWidgetBase):
+ """
+ Manage the edition of a text using TinyMCE
+ """
+ class Media:
+ js = FULL_TINY_JS
+
+
+class TextareaAdminWidget(TextareaWidgetBase):
+ class Media:
+ js = ADMIN_TINY_JS
+
+
+class DatePickerWidget(forms.TextInput):
+ """
+ Manage the edition of dates.
+ JQuery and Jquery-UI are already loaded by default so don't include
+ them in Media files.
+ """
+ def render(self, *args, **kwargs):
+ rendered = super(DatePickerWidget, self).render(*args, **kwargs)
+ rendered += u"\n<script type='text/javascript'>\n"\
+ u" $(function() {$('#%s').datepicker("\
+ u"{ dateFormat: 'yy-mm-dd' });});\n"\
+ u"</script>\n" % kwargs['attrs']['id']
+ return mark_safe(rendered)
+
+
+class JQueryAutoComplete(forms.TextInput):
+ TEMPLATE = "chimere/blocks/JQueryAutoComplete.html"
+
+ def __init__(self, slug, options={}, attrs={}):
+ """
+ Source can be a list containing the autocomplete values or a
+ string containing the url used for the request.
+ """
+ self.options = None
+ self.attrs = {}
+ self.slug = slug
+ if len(options) > 0:
+ self.options = JSONEncoder().encode(options)
+ self.attrs.update(attrs)
+
+ def get_source(self):
+ # Strange... to be fixed
+ source = reverse('chimere:property-choices',
+ kwargs={'property_slug': self.slug})
+ return "'{}'".format(source)
+
+ def render(self, name, value=None, attrs=None):
+ attrs_hidden = self.build_attrs(attrs, name=name)
+ attrs_select = self.build_attrs(attrs)
+ selected_value, rendered_value = "", ""
+
+ if value:
+ val = escape(smart_unicode(value))
+ attrs_hidden['value'] = val
+ attrs_select['value'] = val
+ selected_value = val
+ if val:
+ from chimere.models import PropertyModelChoice
+ try:
+ attrs_select['value'] = unicode(
+ PropertyModelChoice.objects.get(
+ pk=value, propertymodel__slug=self.slug))
+ rendered_value = attrs_select['value']
+ except:
+ attrs_select['value'] = ""
+ if 'id' not in self.attrs:
+ attrs_hidden['id'] = 'id_%s' % name
+ attrs_select['id'] = 'id_select_%s' % name
+ if 'class' not in attrs_select:
+ attrs_select['class'] = 'autocomplete'
+ dct = {
+ 'attrs_select': flatatt(attrs_select),
+ 'attrs_hidden': flatatt(attrs_hidden),
+ 'field_id': name,
+ 'min_field_id': name.replace('_', ''),
+ 'options': self.options,
+ 'source': self.get_source(),
+ 'selected_value': selected_value,
+ 'rendered_value': rendered_value
+ }
+
+ return mark_safe(
+ render_to_string(self.TEMPLATE, dct))
+
+
+class NominatimWidget(forms.TextInput):
+ class Media:
+ js = ["%schimere/js/nominatim-widget.js" % settings.STATIC_URL]
+
+ def render(self, name, value, attrs=None, area_name=''):
+ dct = {'id': name, 'nominatim_url': settings.NOMINATIM_URL,
+ 'label': _(u"Street, City, Country")}
+
+ tpl = u"""
+<input type='hidden' name='nominatim_%(id)s_lat' id='nominatim_%(id)s_lat'/>
+<input type='hidden' name='nominatim_%(id)s_lon' id='nominatim_%(id)s_lon'/>
+<input type='text' class='nominatim-widget' name='nominatim_%(id)s'
+ id='nominatim_%(id)s' value=""/>
+<label class='nominatim-label' id='nominatim_%(id)s_label'>&nbsp;</label>
+<script type='text/javascript'>
+var default_nominatim_lbl = "%(label)s";
+var nominatim_url = "%(nominatim_url)s";
+$("#nominatim_%(id)s").val(default_nominatim_lbl);
+$("#nominatim_%(id)s").click(function(){
+ $("#nominatim_%(id)s").val('');
+});
+</script>
+""" % dct
+ return mark_safe(tpl)
+
+
+class PointChooserWidget(forms.TextInput):
+ """
+ Manage the edition of point on a map
+ """
+ class Media:
+ css = {
+ "all": settings.MAP_CSS_URLS +
+ ["%schimere/css/forms.css" % settings.STATIC_URL]
+ }
+ js = settings.MAP_JS_URLS + list(settings.JQUERY_JS_URLS) + \
+ ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL]
+
+ def render(self, name, value, attrs=None, area_name='', initialized=True):
+ '''
+ Render a map and latitude, longitude information field
+ '''
+ val = ''
+ value_x, value_y = 0, 0
+ if value:
+ val = str(value)
+ if hasattr(value, 'x') and hasattr(value, 'y'):
+ value_x, value_y = value.x, value.y
+ elif isinstance(value, unicode) and value.startswith('POINT('):
+ try:
+ value_x, value_y = value.split('(')[1][:-1].split(' ')
+ value_x, value_y = float(value_x), float(value_y)
+ except:
+ value = None
+ else:
+ value = None
+ map_layers, default_area, zoom = get_map_layers(area_name,
+ get_area_zoom=True)
+ extra_js = [extra_js for n, js, default, extra_js in map_layers
+ if extra_js]
+ map_layers = [js for n, js, default, xtra_js in map_layers if js]
+ # TODO: manage area
+ return mark_safe(
+ render_to_string('chimere/blocks/live_coordinates.html',
+ {'lat': _("Latitude"),
+ 'value_y': value_y,
+ 'lon': _("Longitude"),
+ 'value_x': value_x,
+ 'name': name,
+ 'val': val,
+ 'initialized': initialized,
+ 'extra_js': "\n".join(extra_js),
+ 'isvalue': bool(value),
+ 'default_area': "true" if default_area
+ else "false",
+ }) %
+ (settings.STATIC_URL,
+ settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
+ settings.CHIMERE_EPSG_PROJECTION,
+ "[{}, {}]".format(settings.CHIMERE_DEFAULT_CENTER[0],
+ settings.CHIMERE_DEFAULT_CENTER[1]),
+ settings.CHIMERE_DEFAULT_ZOOM,
+ settings.STATIC_URL,
+ ", ".join(map_layers),
+ zoom
+ )
+ )
+
+
+class HiddenPointChooserWidget(PointChooserWidget):
+ """
+ OpenLayers doesn't initialize well on an hidden field so specific JS
+ must be loaded.
+ """
+ def render(self, *args, **kwargs):
+ kwargs['initialized'] = False
+ return super(HiddenPointChooserWidget, self).render(*args, **kwargs)
+
+
+class PointField(models.PointField):
+ '''
+ Set the widget for the form field
+ '''
+ def __init__(self, *args, **kwargs):
+ self.widget = kwargs.pop('widget') if 'widget' in kwargs \
+ else PointChooserWidget
+ return super(PointField, self).__init__(*args, **kwargs)
+
+ def formfield(self, **keys):
+ defaults = {'widget': self.widget}
+ keys.update(defaults)
+ return super(PointField, self).formfield(**keys)
+
+ def clean(self, value, instance=None):
+ if len(value) != 2 and self.required:
+ raise forms.ValidationError(_("Invalid point"))
+ return value
+
+
+class RouteChooserWidget(forms.TextInput):
+ """
+ Manage the edition of route on a map
+ """
+ class Media:
+ css = {
+ "all": settings.MAP_CSS_URLS +
+ ["%schimere/css/forms.css" % settings.STATIC_URL]
+ }
+ js = settings.MAP_JS_URLS + list(settings.JQUERY_JS_URLS) + \
+ ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL,
+ "%schimere/js/edit_route_map.js" % settings.STATIC_URL,
+ "%schimere/js/base.js" % settings.STATIC_URL]
+
+ def render(self, name, value, attrs=None, area_name='', routefile_id=None):
+ '''
+ Render a map and latitude, longitude information field
+ '''
+ tpl = getMapJS(area_name)
+ map_layers, default_area = get_map_layers(area_name)
+ extra_js = [extra_js for n, js, default, extra_js in map_layers
+ if extra_js]
+ map_layers = [js for nm, js, default, ext_js in map_layers]
+ js = u"""
+ var resolutions;
+ var zoomOffset;
+
+ var extra_url = "%s";
+ epsg_display_projection = 'EPSG:%s';
+ var EPSG_DISPLAY_PROJECTION = epsg_display_projection;
+ epsg_projection = 'EPSG:%s';
+ var EPSG_PROJECTION = epsg_projection;
+ centerLonLat = ol.proj.transform([%f %f],
+ epsg_display_projection, epsg_projection);
+ var CENTER_LONLAT = centerLonLat;
+ var DEFAULT_ZOOM = %s;
+ var chimere_init_options = {};
+ %s
+ chimere_init_options["map_layers"] = [%s];
+ chimere_init_options['dynamic_categories'] = false;
+ chimere_init_options['edition'] = true;
+ chimere_init_options['edition_type_is_route'] = true;
+ chimere_init_options["checked_categories"] = [];
+ """ % (reverse("chimere:index"),
+ settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
+ settings.CHIMERE_EPSG_PROJECTION,
+ settings.CHIMERE_DEFAULT_CENTER[0],
+ settings.CHIMERE_DEFAULT_CENTER[1],
+ settings.CHIMERE_DEFAULT_ZOOM,
+ u"\n".join(extra_js),
+ u", ".join(map_layers))
+ if default_area:
+ js += u"chimere_init_options['selected_map_layer'] = %d;\n" % \
+ default_area
+ tpl = u"<script type='text/javascript'><!--\n"\
+ u"%s// !--></script>\n" % js
+ # TODO: manage area
+ help_create = ''
+ if not value:
+ help_create = u"<h3>%s</h3>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n" % (
+ _(u"Creation mode"),
+ _(u"To start drawing the route click on the toggle "
+ u"button: \"Draw\"."),
+ _(u"Then click on the map to begin the drawing."),
+ _(u"You can add points by clicking again."),
+ _(u"To finish the drawing double click. When the drawing "
+ u"is finished you can edit it."),
+ _(u"While creating to undo a drawing click again on the "
+ u"toggle button \"Stop drawing\"."))
+ help_modify = u"<h3>%s</h3>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n"\
+ u"<p>%s</p>\n" % (
+ _(u"Modification mode"),
+ _(u"To move a point click on it and drag it to the desired "
+ u"position."),
+ _(u"To delete a point move the mouse cursor over it and press "
+ u"the \"d\" or \"Del\" key."),
+ _(u"To add a point click in the middle of a segment and drag "
+ u"the new point to the desired position"))
+ if not value:
+ # upload a file
+ tpl += u"<script type='text/javascript'><!--\n"\
+ u" var error_msg = \"%s\";"\
+ u"// --></script>" % \
+ _(u"Give a name and set category before uploading a file.")
+ tpl += u'<div id="upload"><a href="#" class="upload-button" '\
+ u'onclick="uploadFile(error_msg);return false;">%s</a></div>' \
+ % (_(u"Upload a route file (GPX or KML)"))
+ tpl += u"\n<p id='draw-or'>%s</p>\n" % _(u"or")
+ tpl += u"<div id='draw-label'><div id='draw-toggle-off' "\
+ u"onclick='toggleDraw();'>\n"\
+ u"<a href='#' onclick='return false;'>%s</a></div>"\
+ u"</div>\n"\
+ u"<hr class='spacer'/>" % (_(u"Start \"hand\" drawing"))
+ style = ''
+ if value:
+ style = " style='display:block'"
+ tpl += u"\n<div class='help-route' id='help-route-modify'%s>%s</div>"\
+ u"\n<hr class='spacer'/>\n"\
+ u"<input type='hidden' name='%s' id='id_%s' value=\"%s\"/>\n"\
+ u"<input type='hidden' name='associated_file_id' "\
+ u"id='id_associated_file_id' value=\"%s\"/>\n" % (
+ style, help_modify, name, name, value, routefile_id)
+ if value:
+ tpl += u"\n<div id='map_edit'></div>"
+ else:
+ tpl += "\n<div class='help-route' id='help-route-create'>%s</div>"\
+ % help_create
+ tpl += \
+ u"\n<div id='layerSwitcher'></div>\n<div id='map_edit'>\n"\
+ u" <div class='map_button'>\n"\
+ u" <a href='#' id='button-move-map' class='toggle-button "\
+ u"toggle-button-active' "\
+ u"onclick='toggleDrawOff();return false;'>%s</a>\n"\
+ u"<a href='#' id='button-draw-map' class='toggle-button "\
+ u"toggle-button-inactive' "\
+ u"onclick='toggleDrawOn();return false;'>%s</a></div>\n"\
+ u" </div>" % (_(u"Move on the map"), _(u"Draw"))
+ tpl += u"<script type='text/javascript'><!--\n"
+ if not value:
+ tpl += u"jQuery('#map_edit').hide();"
+ if value:
+ tpl += u"jQuery('#map_edit').chimere(chimere_init_options);\n"
+ val = value
+ if type(value) == unicode:
+ try:
+ val = fromstr(value)
+ except:
+ pass
+ if hasattr(val, 'json'):
+ tpl += u"\nvar geometry='%s';\n"\
+ u"jQuery('#map_edit').chimere('initFeature', geometry);"\
+ % val.json
+ tpl += u"\n// --></script>\n"
+ return mark_safe(tpl)
+
+
+class RouteField(models.LineStringField):
+ '''
+ Set the widget for the form field
+ '''
+ def formfield(self, **keys):
+ defaults = {'widget': RouteChooserWidget}
+ keys.update(defaults)
+ return super(RouteField, self).formfield(**keys)
+
+
+class AreaWidget(forms.TextInput):
+ """
+ Manage the edition of an area on the map
+ """
+ class Media:
+ css = {
+ "all": settings.MAP_CSS_URLS +
+ ["%schimere/css/forms.css" % settings.STATIC_URL]
+ }
+ js = settings.MAP_JS_URLS + [
+ "%schimere/js/edit_area.js" % settings.STATIC_URL,
+ "%schimere/js/base.js" % settings.STATIC_URL]
+
+ def get_bounding_box_from_value(self, value):
+ '''
+ Return upper left lat/lon and lower lat/lon from the input value
+ '''
+ upper_left_lat, upper_left_lon = 0, 0
+ lower_right_lat, lower_right_lon = 0, 0
+ if not value:
+ return upper_left_lat, upper_left_lon, lower_right_lat, \
+ lower_right_lon
+ if len(value) == 2:
+ upper_left = value[0]
+ lower_right = value[1]
+ if hasattr(upper_left, 'x') and hasattr(upper_left, 'y'):
+ upper_left_lon, upper_left_lat = upper_left.x, upper_left.y
+ elif len(upper_left) == 2:
+ try:
+ upper_left_lon = float(upper_left[0])
+ upper_left_lat = float(upper_left[1])
+ except ValueError:
+ pass
+ if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'):
+ lower_right_lon, lower_right_lat = lower_right.x, \
+ lower_right.y
+ elif len(lower_right) == 2:
+ lower_right_lon, lower_right_lat = lower_right
+ try:
+ lower_right_lon = float(lower_right[0])
+ lower_right_lat = float(lower_right[1])
+ except ValueError:
+ pass
+ return upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon
+
+ def render(self, name, value, attrs=None, initialized=True):
+ """
+ Render a map
+ """
+ upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon = \
+ self.get_bounding_box_from_value(value)
+ tpl = getMapJS()
+ tpl += u"</div>\n"\
+ u"<input type='hidden' name='upper_left_lat' id='upper_left_lat' "\
+ u"value='%f'/>\n"\
+ u"<input type='hidden' name='upper_left_lon' id='upper_left_lon' "\
+ u"value='%f'/>\n"\
+ u"<input type='hidden' name='lower_right_lat' id='lower_right_lat'"\
+ u" value='%f'/>\n"\
+ u"<input type='hidden' name='lower_right_lon' id='lower_right_lon'"\
+ u" value='%f'/>\n" % (
+ upper_left_lat, upper_left_lon, lower_right_lat,
+ lower_right_lon)
+ help_msg = _(u"Hold CTRL, click and drag to select area on the map")
+ tpl += u"<p class='help-osm'>%s</p>\n" % help_msg
+ tpl += u"<script type='text/javascript'>\n"
+ tpl += u"function init_map_form (){\ninit('map_edit_area');\n"
+ if value:
+ tpl += u"extent = ol.proj.transformExtent([%f, %f, %f, %f], "\
+ u"epsg_display_projection, epsg_projection);\n"\
+ u"initArea(extent);\n" % (
+ upper_left_lon, upper_left_lat, lower_right_lon,
+ lower_right_lat)
+ tpl += u"}\n"
+ if initialized:
+ tpl += u"$(document).ready(function($) {init_map_form()});\n"
+ tpl += u"</script>\n"
+ tpl += u"<div id='map_edit_area'>\n"
+ return mark_safe(tpl)
+
+ def value_from_datadict(self, data, files, name):
+ """
+ Return the appropriate values
+ """
+ values = []
+ for keys in (('upper_left_lon', 'upper_left_lat',),
+ ('lower_right_lon', 'lower_right_lat')):
+ value = []
+ for key in keys:
+ val = data.get(key, None)
+ if not val:
+ return []
+ value.append(val)
+ values.append(value)
+ return values
+
+
+class PolygonChooserWidget(forms.TextInput):
+ """
+ Manage the edition of polygon on a map
+ """
+ class Media:
+ css = {
+ "all": settings.MAP_CSS_URLS +
+ ["%schimere/css/forms.css" % settings.STATIC_URL]
+ }
+ js = settings.MAP_JS_URLS + list(settings.JQUERY_JS_URLS) + \
+ ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL]
+
+ def render(self, name, value, attrs=None, area_name='', initialized=True):
+ val = ''
+ if value:
+ val = str(value)
+ map_layers, default_area, zoom = get_map_layers(area_name,
+ get_area_zoom=True)
+ extra_js = [extra_js for n, js, default, extra_js in map_layers
+ if extra_js]
+ map_layers = [js for n, js, default, ext_js in map_layers
+ if 'OpenLayers' not in js]
+ tpl = render_to_string(
+ 'chimere/blocks/polygon_edit.html',
+ {'name': name, 'val': val, 'initialized': initialized,
+ 'isvalue': bool(value),
+ 'default_area': "true" if default_area else "false",
+ 'value': value
+ }
+ )
+ return mark_safe(tpl.format(
+ static_url=settings.STATIC_URL,
+ display_projection=settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
+ projection=settings.CHIMERE_EPSG_PROJECTION,
+ center=list(settings.CHIMERE_DEFAULT_CENTER),
+ zoom=zoom,
+ map_layers=u", ".join(map_layers),
+ extra_js=u"\n".join(extra_js),
+ ))
+
+
+class PolygonField(models.PolygonField):
+ '''
+ Set the widget for the form field
+ '''
+ def formfield(self, **keys):
+ defaults = {'widget': PolygonChooserWidget}
+ keys.update(defaults)
+ return super(PolygonField, self).formfield(**keys)
+
+RE_XAPI = re.compile(
+ '(node|way)\[(.*=.*)\]\[bbox='
+ '(-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*)\]')
+
+
+class ImportFiltrWidget(AreaWidget):
+ """
+ Manage the edition of the import source field
+ """
+ class Media:
+ css = {
+ "all": settings.MAP_CSS_URLS +
+ ["%schimere/css/forms.css" % settings.STATIC_URL]
+ }
+ js = settings.MAP_JS_URLS + [
+ "%schimere/js/edit_area.js" % settings.STATIC_URL,
+ "%schimere/js/base.js" % settings.STATIC_URL]
+
+ def render(self, name, value, attrs=None):
+ """
+ Render a map
+ """
+ tpl = super(ImportFiltrWidget, self).render(name, value, attrs,
+ initialized=False)
+ tpl += u"</div><hr class='spacer'/>"
+ vals = {'lbl': _(u"Type:"), 'name': name, 'node': _(u"Node"),
+ 'way': _(u"Way")}
+ vals['way_selected'] = ' checked="checked"'\
+ if self.xapi_type == 'way' else ''
+ vals['node_selected'] = ' checked="checked"'\
+ if self.xapi_type == 'node' else ''
+ tpl += u"<div class='input-osm'><label>%(lbl)s</label>"\
+ u"<input type='radio' name='id_%(name)s_type' "\
+ u"id='id_%(name)s_node' value='node'%(node_selected)s/> "\
+ u"<label for='id_%(name)s_node'>"\
+ u"%(node)s</label> <input type='radio' name='id_%(name)s_type' "\
+ u"id='id_%(name)s_way' value='way'%(way_selected)s/> <label "\
+ u"for='id_%(name)s_way'>%(way)s</label></div>" % vals
+ help_msg = _(
+ u"Enter an OSM \"tag=value\" string such as "
+ u"\"amenity=pub\". A list of common tag is available "
+ u"<a href='https://wiki.openstreetmap.org/wiki/Map_Features' "
+ u" target='_blank'>here</a>.")
+ tpl += u"<p class='help-osm'>%s</p>\n" % help_msg
+ tpl += u"<div class='input-osm'><label for='id_%s_tag'>%s</label>"\
+ u"<input type='text' id='id_%s_tag' value=\"%s\"/></div>" % (
+ name, _(u"Tag:"), name, self.xapi_tag)
+ tpl += u"<script type='text/javascript'>\n"
+ tpl += u"var default_xapi='%s';" % settings.CHIMERE_XAPI_URL
+ tpl += u'var msg_missing_area = "%s";' % \
+ _(u"You have to select an area.")
+ tpl += u'var msg_missing_type = "%s";' % \
+ _(u"You have to select a type.")
+ tpl += u'var msg_missing_filtr = "%s";' % \
+ _(u"You have to insert a filter tag.")
+ tpl += u"</script>\n"
+ help_msg = _(u"If you change the above form don't forget to refresh "
+ u"before submit!")
+ tpl += u"<p class='help-osm errornote'>%s</p>\n" % help_msg
+ help_msg = _(u"You can put a Folder name of the KML file to filter on "
+ u"it.")
+ tpl += u"<p class='help-kml'>%s</p>\n" % help_msg
+ if not value:
+ value = ''
+ tpl += u"<div><textarea id='id_%s' name='id_%s' "\
+ u">%s</textarea> <input type='button' id='id_refresh_%s' "\
+ u"value='%s' class='input-osm'/>" % (name, name, value, name,
+ _(u"Refresh"))
+ return mark_safe(tpl)
+
+ def value_from_datadict(self, data, files, name):
+ """
+ Return the appropriate values
+ """
+ return data.get('id_' + name, None)
+
+ def get_bounding_box_from_value(self, value):
+ '''
+ Return upper left lat/lon, lower lat/lon from the input value.
+ Get also xapi type and xapi tag
+ '''
+ upper_left_lat, upper_left_lon = 0, 0
+ lower_right_lat, lower_right_lon = 0, 0
+ self.xapi_type, self.xapi_tag, self.bounding_box = None, '', None
+ if not value:
+ return upper_left_lat, upper_left_lon, lower_right_lat, \
+ lower_right_lon
+ xapi_m = RE_XAPI.match(value)
+ if not xapi_m:
+ return upper_left_lat, upper_left_lon, lower_right_lat, \
+ lower_right_lon
+ # as the regexp pass, we could be pretty confident
+ self.xapi_type, self.xapi_tag, self.bounding_box = xapi_m.groups()
+ upper_left_lon, lower_right_lat, lower_right_lon, upper_left_lat = \
+ self.bounding_box.split(',')
+ return float(upper_left_lat), float(upper_left_lon), \
+ float(lower_right_lat), float(lower_right_lon)
+
+
+class AreaField(forms.MultiValueField):
+ '''
+ Set the widget for the form field
+ '''
+ widget = AreaWidget
+
+ def compress(self, data_list):
+ if not data_list:
+ return None
+ return data_list
+
+
+class MultiSelectWidget(forms.SelectMultiple):
+ class Media:
+ css = {'all': list(settings.JQUERY_CSS_URLS) + [
+ settings.STATIC_URL + 'bsmSelect/css/jquery.bsmselect.css',
+ settings.STATIC_URL + 'bsmSelect/css/jquery.bsmselect.custom.css',
+ ]
+ }
+ js = list(settings.JQUERY_JS_URLS) + [
+ settings.STATIC_URL + 'bsmSelect/js/jquery.bsmselect.js',
+ settings.STATIC_URL +
+ 'bsmSelect/js/jquery.bsmselect.compatibility.js',
+ ]
+
+ def render(self, name, value, attrs=None):
+ rendered = super(MultiSelectWidget, self).render(name, value, attrs)
+ rendered += u"<hr class='spacer'/><script type='text/javascript'>\n"\
+ u"$.bsmSelect.conf['title'] = \"%(title)s\";\n"\
+ u"$(\"#id_%(name)s\").bsmSelect({\n"\
+ u" removeLabel: '<strong>X</strong>',\n"\
+ u" containerClass: 'bsmContainer',\n"\
+ u" listClass: 'bsmList-custom',\n"\
+ u" listItemClass: 'bsmListItem-custom',\n"\
+ u" listItemLabelClass: 'bsmListItemLabel-custom',\n"\
+ u" removeClass: 'bsmListItemRemove-custom'\n"\
+ u"});\n"\
+ u"</script>\n" % {'name': name, 'title': _("Select...")}
+ return mark_safe(rendered)
+
+
+class SelectMultipleField(models.ManyToManyField):
+ '''
+ Set the widget for the category field
+ '''
+ def formfield(self, **keys):
+ self.help_text = ""
+ defaults = {'widget': MultiSelectWidget}
+ keys.update(defaults)
+ return super(SelectMultipleField, self).formfield(**keys)
+
+from south.modelsinspector import add_introspection_rules
+add_introspection_rules([], ["^chimere\.widgets\.PointField"])
+add_introspection_rules([], ["^chimere\.widgets\.SelectMultipleField"])
+add_introspection_rules([], ["^chimere\.widgets\.RouteField"])
+add_introspection_rules([], ["^chimere\.widgets\.PolygonField"])