diff options
Diffstat (limited to 'chimere/main/widgets.py')
| -rw-r--r-- | chimere/main/widgets.py | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/chimere/main/widgets.py b/chimere/main/widgets.py new file mode 100644 index 0000000..6b5e544 --- /dev/null +++ b/chimere/main/widgets.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2008 É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 django import forms +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext as _ +from django.contrib.gis.geos import fromstr + +from chimere import settings +from django.contrib.gis.db import models + +URL_OSM_CSS = ["http://www.openlayers.org/api/theme/default/style.css"] +URL_OSM_JS = ["http://www.openlayers.org/api/OpenLayers.js", + "http://www.openstreetmap.org/openlayers/OpenStreetMap.js"] + +def getMapJS(): + '''Variable initialization for drawing the map + ''' + # projection, center and bounds definitions + js = u"var epsg_display_projection = new OpenLayers.Projection('EPSG:%d')\ +;\n" % settings.EPSG_DISPLAY_PROJECTION + js += u"var epsg_projection = new OpenLayers.Projection('EPSG:%d');\n" % \ + settings.EPSG_PROJECTION + js += u"var centerLonLat = new OpenLayers.LonLat(%f,\ +%f).transform(epsg_display_projection, epsg_projection);\n" % \ + settings.DEFAULT_CENTER + js += u"var media_path = '%s';\n" % settings.MEDIA_URL + js += u"var map_layer = %s;\n" % settings.MAP_LAYER + js += u"var restricted_extent;\n" + if settings.RESTRICTED_EXTENT: + restricted_extent_str = [str(coord) \ + for coord in settings.RESTRICTED_EXTENT] + js += u"restricted_extent = new OpenLayers.Bounds(%s);\n" %\ + ", ".join(restricted_extent_str) + js = u"""<script type="text/javascript"><!-- +%s// !--></script> +""" % js + return js + +class TextareaWidget(forms.Textarea): + """ + Manage the edition of a text using TinyMCE + """ + class Media: + js = ["%stiny_mce.js" % settings.TINYMCE_URL, + "%stextareas.js" % settings.MEDIA_URL,] + +class PointChooserWidget(forms.TextInput): + """ + Manage the edition of point on a map + """ + class Media: + css = { + "all": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] + } + js = URL_OSM_JS + ["%sedit_map.js" % settings.MEDIA_URL, + "%sbase.js" % settings.MEDIA_URL,] + + def render(self, name, value, attrs=None): + ''' + Render a map and latitude, longitude information field + ''' + val = '0' + 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 + tpl = getMapJS() + tpl += u'<script src="%sedit_map.js"></script>\n' % settings.MEDIA_URL + tpl += u"""<div id='map_edit'></div> +<div id='live_lonlat'> +<p><label for='live_latitude'>%s</label>\ +<input type='texte' name='live_latitude' id='live_latitude' size='8' \ +disabled='true' value='%f'/></p> +<p><label for='live_longitude'>%s</label><input type='texte' \ +name='live_longitude' id='live_longitude' size='8' disabled='true' \ +value='%f'/></p> +</div> +<input type='hidden' name='%s' id='id_%s' value="%s"/> +""" % (_("Latitude"), value_y, _("Longitude"), value_x, name, name, val) + tpl += """<script type='text/javascript'><!-- +init();""" + if value: + tpl += '''var mylonlat = new OpenLayers.LonLat(%f,%f); +putMarker(mylonlat.transform(epsg_display_projection, + map.getProjectionObject()).clone(), true); +''' % (value_x, value_y) + tpl += """// --></script> +<hr class='spacer'/> +""" + return mark_safe(tpl) + +class PointField(models.PointField): + ''' + Set the widget for the form field + ''' + def formfield(self, **keys): + defaults = {'widget': PointChooserWidget} + keys.update(defaults) + return super(PointField, self).formfield(**keys) + + def clean(self, value): + if len(value) != 2 and self.required: + raise ValidationError(_("Invalid point")) + return value + +class RouteChooserWidget(forms.TextInput): + """ + Manage the edition of route on a map + """ + class Media: + css = { + "all": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] + } + js = ["%sedit_route_map.js" % settings.MEDIA_URL, + "%sbase.js" % settings.MEDIA_URL,] + URL_OSM_JS + + def render(self, name, value, attrs=None): + ''' + Render a map and latitude, longitude information field + ''' + tpl = getMapJS() + help_create = '' + if not value: + help_create = """<h3>%s</h3> +<p>%s</p> +<p>%s</p> +<p>%s</p> +<p>%s</p> +<p>%s</p>""" % (_("Creation mode"), +_("To start drawing the route click on the toggle button : \"Start drawing\"."), +_("Then click on the map to begin the drawing."), +_("You can add points by clicking again."), +_("To finish the drawing double click. When the drawing is finished you can \ +edit it."), +_("While creating to undo a drawing click again on the toggle button \"Stop \ +drawing\".")) + help_modify = """<h3>%s</h3> +<p>%s</p> +<p>%s</p> +<p>%s</p>""" % (_("Modification mode"), +_("To move a point click on it and drag it to the desired position."), +_("To delete a point move the mouse cursor over it and press the \"d\" key."), +_("To add a point click in the middle of a segment and drag the new point to \ +the desired position")) + tpl += u'<script src="%sedit_route_map.js"></script>\n' % \ + settings.MEDIA_URL + if not value: + tpl += u"""<div id='draw-toggle-off' class='toggle-button' \ +onclick='toggleDrawOn();'>%s</div> +<div id='draw-toggle-on' class='toggle-button' \ +onclick='toggleDrawOff();'>%s</div> +<hr class='spacer'/>""" % (_("Start drawing"), _("Stop drawing")) + tpl += """ +<div id='map_edit'></div>""" + if not value: + tpl += ''' +<div class='help-route' id='help-route-create'>%s</div>''' % help_create + style = '' + if value: + style = " style='display:block'" + tpl += """ +<div class='help-route' id='help-route-modify'%s>%s</div> +<hr class='spacer'/> +<input type='hidden' name='%s' id='id_%s' value="%s"/> +""" % (style, help_modify, name, name, value) + tpl += """<script type='text/javascript'><!-- +init();""" + if value: + val = value + if type(value) == unicode: + try: + val = fromstr(value) + except: + pass + if hasattr(val, 'json'): + tpl += """ +var geometry='%s'; +initFeature(geometry);""" % val.json + tpl += """ +// --></script> +""" + 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": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] + } + js = URL_OSM_JS + ["%sedit_area.js" % settings.MEDIA_URL, + "%sbase.js" % settings.MEDIA_URL,] + + def render(self, name, value, attrs=None): + """ + Render a map + """ + upper_left_lat, upper_left_lon = 0, 0 + lower_right_lat, lower_right_lon = 0, 0 + if value: + 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 + if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'): + lower_right_lon, lower_right_lat = lower_right.x, \ + lower_right.y + tpl = getMapJS() + tpl += u"""<div id='map_edit'></div> +<input type='hidden' name='upper_left_lat' id='upper_left_lat' value='%f'/> +<input type='hidden' name='upper_left_lon' id='upper_left_lon' value='%f'/> +<input type='hidden' name='lower_right_lat' id='lower_right_lat' value='%f'/> +<input type='hidden' name='lower_right_lon' id='lower_right_lon' value='%f'/> +""" % (upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon) + tpl += """<script type='text/javascript'><!-- +init();""" + if value: + tpl += """var extent = new OpenLayers.Bounds(%f, %f, %f, %f); +map.zoomToExtent(extent, true);""" % (upper_left_lat, upper_left_lon, lower_right_lat, + lower_right_lon) + tpl += """// --></script> +<hr class='spacer'/> +""" + return mark_safe(tpl) + + def value_from_datadict(self, data, files, name): + """ + Return the appropriate values + """ + values = [] + for keys in (('upper_left_lat', 'upper_left_lon',), + ('lower_right_lat', 'lower_right_lon')): + value = [] + for key in keys: + val = data.get(key, None) + if not val: + return [] + value.append(val) + values.append(value) + return values + +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 |
