diff options
Diffstat (limited to 'chimere/widgets.py')
-rw-r--r-- | chimere/widgets.py | 551 |
1 files changed, 373 insertions, 178 deletions
diff --git a/chimere/widgets.py b/chimere/widgets.py index f501e23..2672ba0 100644 --- a/chimere/widgets.py +++ b/chimere/widgets.py @@ -20,34 +20,36 @@ """ Extra widgets and fields """ -from django import conf + +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 -from django.utils.encoding import force_unicode -from django.utils.html import conditional_escape +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 = new OpenLayers.Projection('EPSG:%d')"\ - u";\n" % settings.CHIMERE_EPSG_DISPLAY_PROJECTION - js += u"OpenLayers.ImgPath = '%schimere/img/';\n" % settings.STATIC_URL - js += u"var epsg_projection = new OpenLayers.Projection('EPSG:%d');\n" % \ - settings.CHIMERE_EPSG_PROJECTION - js += u"var centerLonLat = new OpenLayers.LonLat(%f,"\ - u"%f).transform(epsg_display_projection, epsg_projection);\n" % \ - settings.CHIMERE_DEFAULT_CENTER + 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 @@ -59,7 +61,8 @@ def getMapJS(area_name=''): u"%s// !--></script>\n" % js return js -def get_map_layers(area_name=''): + +def get_map_layers(area_name='', get_area_zoom=False): from chimere.models import Area area = None if area_name: @@ -74,8 +77,9 @@ def get_map_layers(area_name=''): pass map_layers, default = [], None if area and area.layers.count(): - map_layers = [[layer.name, layer.layer_code, False] - for layer in area.layers.order_by('arealayers__order').all()] + 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] @@ -87,34 +91,46 @@ def get_map_layers(area_name=''): map_layers[0][2] = True elif settings.CHIMERE_DEFAULT_MAP_LAYER: map_layers = [(_(u"Default layer"), settings.CHIMERE_DEFAULT_MAP_LAYER, - True)] + True, '')] else: - map_layers = [(u"Mapnik", "new OpenLayers.Layer.OSM.Mapnik('Mapnik')", - True)] - return map_layers, default + map_layers = [(u"Mapquest", """new ol.layer.Tile({ + style: 'Road', + source: new ol.source.MapQuest({layer: '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,] - } + 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'] + 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 @@ -127,13 +143,18 @@ class ButtonRadioInput(RadioInput): 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 @@ -143,22 +164,36 @@ class ButtonSelectWidget(forms.RadioSelect): 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'] + 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,] + 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']: @@ -169,14 +204,19 @@ class TextareaWidgetBase(forms.Textarea): 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 = ["%stiny_mce.js" % settings.TINYMCE_URL] + 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']: @@ -196,18 +236,19 @@ tinyMCE.init({ """ return mark_safe(rendered) + class TextareaWidget(TextareaWidgetBase): """ Manage the edition of a text using TinyMCE """ class Media: - js = ["%stiny_mce.js" % settings.TINYMCE_URL, - "%schimere/js/textareas.js" % settings.STATIC_URL,] + js = FULL_TINY_JS + class TextareaAdminWidget(TextareaWidgetBase): class Media: - js = ["%stiny_mce.js" % settings.TINYMCE_URL, - "%schimere/js/textareas_admin.js" % settings.STATIC_URL,] + js = ADMIN_TINY_JS + class DatePickerWidget(forms.TextInput): """ @@ -218,18 +259,85 @@ class DatePickerWidget(forms.TextInput): def render(self, *args, **kwargs): rendered = super(DatePickerWidget, self).render(*args, **kwargs) rendered += u"\n<script type='text/javascript'>\n"\ - u" $(function() {$('#%s').datepicker({ dateFormat: 'yy-mm-dd' });});\n"\ - u"</script>\n" % kwargs['attrs']['id'] + 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=""/> +<input type='text' class='nominatim-widget' name='nominatim_%(id)s' + id='nominatim_%(id)s' value=""/> <label class='nominatim-label' id='nominatim_%(id)s_label'> </label> <script type='text/javascript'> var default_nominatim_lbl = "%(label)s"; @@ -239,21 +347,21 @@ $("#nominatim_%(id)s").click(function(){ $("#nominatim_%(id)s").val(''); }); </script> -""" % {'id':name, 'nominatim_url':settings.NOMINATIM_URL, - 'label':_(u"Street, City, Country")} +""" % dct return mark_safe(tpl) + class PointChooserWidget(forms.TextInput): """ Manage the edition of point on a map """ class Media: css = { - "all": settings.OSM_CSS_URLS + \ - ["%schimere/css/forms.css" % settings.STATIC_URL,] + "all": settings.MAP_CSS_URLS + + ["%schimere/css/forms.css" % settings.STATIC_URL] } - js = settings.OSM_JS_URLS + list(settings.JQUERY_JS_URLS) + \ - ["%schimere/js/jquery.chimere.js" % 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): ''' @@ -273,9 +381,12 @@ class PointChooserWidget(forms.TextInput): value = None else: value = None - map_layers, default_area = get_map_layers(area_name) - map_layers = [js for n, js, default in map_layers] - #TODO: manage area + 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"), @@ -285,18 +396,23 @@ class PointChooserWidget(forms.TextInput): 'name': name, 'val': val, 'initialized': initialized, + 'extra_js': "\n".join(extra_js), 'isvalue': bool(value), - 'default_area': "true" if default_area else "false", - }) % \ + 'default_area': "true" if default_area + else "false", + }) % (settings.STATIC_URL, settings.CHIMERE_EPSG_DISPLAY_PROJECTION, settings.CHIMERE_EPSG_PROJECTION, - settings.CHIMERE_DEFAULT_CENTER, + "[{}, {}]".format(settings.CHIMERE_DEFAULT_CENTER[0], + settings.CHIMERE_DEFAULT_CENTER[1]), settings.CHIMERE_DEFAULT_ZOOM, settings.STATIC_URL, - ", ".join(map_layers) + ", ".join(map_layers), + zoom ) - ) + ) + class HiddenPointChooserWidget(PointChooserWidget): """ @@ -307,13 +423,14 @@ class HiddenPointChooserWidget(PointChooserWidget): 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 + else PointChooserWidget return super(PointField, self).__init__(*args, **kwargs) def formfield(self, **keys): @@ -323,21 +440,23 @@ class PointField(models.PointField): def clean(self, value, instance=None): if len(value) != 2 and self.required: - raise ValidationError(_("Invalid point")) + raise forms.ValidationError(_("Invalid point")) return value + class RouteChooserWidget(forms.TextInput): """ Manage the edition of route on a map """ class Media: - css = {"all": settings.OSM_CSS_URLS + \ - ["%schimere/css/forms.css" % settings.STATIC_URL,] + css = { + "all": settings.MAP_CSS_URLS + + ["%schimere/css/forms.css" % settings.STATIC_URL] } - js = settings.OSM_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,] + 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): ''' @@ -345,67 +464,80 @@ class RouteChooserWidget(forms.TextInput): ''' tpl = getMapJS(area_name) map_layers, default_area = get_map_layers(area_name) - map_layers = [js for nm, js, default in map_layers] - js = """ + 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"; - OpenLayers.ImgPath = '%schimere/img/'; - var EPSG_DISPLAY_PROJECTION = epsg_display_projection = new OpenLayers.Projection('EPSG:%s'); - var EPSG_PROJECTION = epsg_projection = new OpenLayers.Projection('EPSG:%s'); - var CENTER_LONLAT = centerLonLat = new OpenLayers.LonLat%s.transform(epsg_display_projection, epsg_projection); + 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.STATIC_URL, - settings.CHIMERE_EPSG_DISPLAY_PROJECTION, - settings.CHIMERE_EPSG_PROJECTION, settings.CHIMERE_DEFAULT_CENTER, - settings.CHIMERE_DEFAULT_ZOOM, ", ".join(map_layers)) + """ % (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 += "chimere_init_options['selected_map_layer'] = %d;\n" % \ + 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 + # 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"<p>%s</p>\n"\ - u"<p>%s</p>\n" % (_(u"Creation mode"), - _(u"To start drawing the route click on the toggle button: "\ - u"\"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 is "\ - u"finished you can edit it."), - _(u"While creating to undo a drawing click again on the toggle "\ - u"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 position."), - _(u"To delete a point move the mouse cursor over it and press the "\ - u"\"d\" or \"Del\" key."), - _(u"To add a point click in the middle of a segment and drag the new "\ - u"point to the desired position")) + 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.")) + 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)")) + 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"\ @@ -416,25 +548,26 @@ class RouteChooserWidget(forms.TextInput): 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) + 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='map_edit'>\n"\ - u" <div class='map_button'>\n"\ - u" <a href='#' id='button-move-map' class='toggle-button "\ - u"toggle-button-active' onclick='toggleDrawOff();return false;'>%s"\ - u"</a>\n"\ - u"<a href='#' id='button-draw-map' class='toggle-button "\ - u"toggle-button-inactive' onclick='toggleDrawOn();return false;'>"\ - u"%s</a></div>\n"\ - u" </div>" % (_(u"Move on the map"), _(u"Draw")) + 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();" @@ -453,6 +586,7 @@ class RouteChooserWidget(forms.TextInput): tpl += u"\n// --></script>\n" return mark_safe(tpl) + class RouteField(models.LineStringField): ''' Set the widget for the form field @@ -462,18 +596,19 @@ class RouteField(models.LineStringField): 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.OSM_CSS_URLS + \ - ["%schimere/css/forms.css" % settings.STATIC_URL,] + "all": settings.MAP_CSS_URLS + + ["%schimere/css/forms.css" % settings.STATIC_URL] } - js = settings.OSM_JS_URLS + [ - "%schimere/js/edit_area.js" % settings.STATIC_URL, - "%schimere/js/base.js" % 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): ''' @@ -483,7 +618,7 @@ class AreaWidget(forms.TextInput): lower_right_lat, lower_right_lon = 0, 0 if not value: return upper_left_lat, upper_left_lon, lower_right_lat, \ - lower_right_lon + lower_right_lon if len(value) == 2: upper_left = value[0] lower_right = value[1] @@ -497,7 +632,7 @@ class AreaWidget(forms.TextInput): pass if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'): lower_right_lon, lower_right_lat = lower_right.x, \ - lower_right.y + lower_right.y elif len(lower_right) == 2: lower_right_lon, lower_right_lat = lower_right try: @@ -512,29 +647,29 @@ class AreaWidget(forms.TextInput): Render a map """ upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon = \ - self.get_bounding_box_from_value(value) + 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) + 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"var extent = new OpenLayers.Bounds(%f, %f, %f, %f);\n"\ - u"extent.transform(epsg_display_projection, epsg_projection);\n"\ - u"updateForm(extent);\n"\ - u"area_map.zoomToExtent(extent, true);\n"\ - u"area_map.zoomOut();" % (upper_left_lon, upper_left_lat, - lower_right_lon, lower_right_lat) + 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" @@ -558,8 +693,61 @@ class AreaWidget(forms.TextInput): values.append(value) return values -RE_XAPI = re.compile('(node|way)\[(.*=.*)\]\[bbox='\ - '(-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*)\]') + +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): """ @@ -567,59 +755,61 @@ class ImportFiltrWidget(AreaWidget): """ class Media: css = { - "all": settings.OSM_CSS_URLS + \ - ["%schimere/css/forms.css" % settings.STATIC_URL,] + "all": settings.MAP_CSS_URLS + + ["%schimere/css/forms.css" % settings.STATIC_URL] } - js = settings.OSM_JS_URLS + [ - "%schimere/js/edit_area.js" % settings.STATIC_URL, - "%schimere/js/base.js" % 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) + initialized=False) tpl += u"</div><hr class='spacer'/>" - vals = {'lbl':_(u"Type:"), 'name':name, 'node':_(u"Node"), - 'way':_(u"Way")} + vals = {'lbl': _(u"Type:"), 'name': name, 'node': _(u"Node"), + 'way': _(u"Way")} vals['way_selected'] = ' checked="checked"'\ - if self.xapi_type == 'way' else '' + if self.xapi_type == 'way' else '' vals['node_selected'] = ' checked="checked"'\ - if self.xapi_type == 'node' else '' + 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' id='id_%(name)s_node'"\ - u" value='node'%(node_selected)s/> <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>.") + 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) + 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.") + _(u"You have to select an area.") tpl += u'var msg_missing_type = "%s";' % \ - _(u"You have to select a type.") + _(u"You have to select a type.") tpl += u'var msg_missing_filtr = "%s";' % \ - _(u"You have to insert a filter tag.") + _(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 "\ + 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 "\ + 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><input type='text' id='id_%s' name='id_%s' "\ - u"value=\"%s\"/> <input type='button' id='id_refresh_%s' "\ + 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) @@ -628,7 +818,7 @@ class ImportFiltrWidget(AreaWidget): """ Return the appropriate values """ - return data.get('id_'+name, None) + return data.get('id_' + name, None) def get_bounding_box_from_value(self, value): ''' @@ -640,17 +830,18 @@ class ImportFiltrWidget(AreaWidget): 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 + 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 + 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(',') + self.bounding_box.split(',') return float(upper_left_lat), float(upper_left_lon), \ - float(lower_right_lat), float(lower_right_lon) + float(lower_right_lat), float(lower_right_lon) + class AreaField(forms.MultiValueField): ''' @@ -663,33 +854,36 @@ class AreaField(forms.MultiValueField): 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', - ] + 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...")} + 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 @@ -704,3 +898,4 @@ 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"]) |