diff options
21 files changed, 900 insertions, 129 deletions
| diff --git a/chimere/forms.py b/chimere/forms.py index d3c55ce..20171df 100644 --- a/chimere/forms.py +++ b/chimere/forms.py @@ -35,7 +35,7 @@ from chimere.models import Marker, Route, PropertyModel, Property, Area,\       News, Category, SubCategory, RouteFile, MultimediaFile, MultimediaType, \       PictureFile, Importer  from chimere.widgets import AreaField, PointField, TextareaWidget, \ -                            DatePickerWidget +                            DatePickerWidget, ButtonSelectWidget, NominatimWidget  from datetime import timedelta, datetime, tzinfo @@ -452,3 +452,19 @@ class AreaForm(AreaAdminForm):      class Meta:          model = Area +class RoutingForm(forms.Form): +    transport = forms.ChoiceField(label='', widget=ButtonSelectWidget, +                               choices=settings.CHIMERE_ROUTING_TRANSPORT, +                               initial=settings.CHIMERE_ROUTING_TRANSPORT[0][0]) +    start = forms.CharField(label=_(u"Start"), widget=NominatimWidget) +    end = forms.CharField(label=_(u"Finish"), widget=NominatimWidget) +    speed = forms.ChoiceField(label=_(u"Speed"), choices=[], required=False) + +    def __init__(self, *args, **kwargs): +        super(RoutingForm, self).__init__(*args, **kwargs) +        if not settings.CHIMERE_ROUTING_SPEEDS: +            self.fields.pop('speed') +        for transport in settings.CHIMERE_ROUTING_SPEEDS: +            for speed, lbl in settings.CHIMERE_ROUTING_SPEEDS[transport]: +                self.fields['speed'].widget.choices.append( +                                ("%s_%d" % (transport, speed), lbl)) diff --git a/chimere/locale/fr/LC_MESSAGES/django.po b/chimere/locale/fr/LC_MESSAGES/django.po index 4462ff1..b986cf5 100644 --- a/chimere/locale/fr/LC_MESSAGES/django.po +++ b/chimere/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: 0.2\n"  "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-20 21:17+0200\n" +"POT-Creation-Date: 2012-08-22 14:54+0200\n"  "PO-Revision-Date: 2010-03-20 20:00+0100\n"  "Last-Translator: Étienne Loks <etienne.loks@peacefrogs.net>\n"  "MIME-Version: 1.0\n" @@ -134,6 +134,18 @@ msgstr "Nom"  msgid "Area"  msgstr "Zone" +#: forms.py:461 +msgid "Start" +msgstr "Départ" + +#: forms.py:462 +msgid "Finish" +msgstr "Arrivée :" + +#: forms.py:463 +msgid "Speed" +msgstr "Vitesse" +  #: models.py:50 models.py:124 models.py:151 models.py:244 models.py:453  #: models.py:822 models.py:903  msgid "Available" @@ -630,19 +642,19 @@ msgstr ""  msgid "Bad param"  msgstr "Mauvais paramètre" -#: views.py:229 +#: views.py:236  msgid "There are missing field(s) and/or errors in the submited form."  msgstr "Il y a des champs manquants ou des erreurs dans ce formulaire." -#: views.py:312 +#: views.py:319  msgid "Bad file. Please check it with an external software."  msgstr "Fichier incohérent. Merci de le vérifier avec un logiciel externe." -#: views.py:434 +#: views.py:441  msgid "Comments/request on the map"  msgstr "Commentaires/requètes sur la carte" -#: views.py:437 +#: views.py:444  msgid ""  "Thank you for your contribution. It will be taken into account. If you have "  "left your email you may be contacted soon for more details." @@ -651,48 +663,48 @@ msgstr ""  "laissé votre courriel vous serez peut-être contacté bientôt pour plus de "  "détails." -#: views.py:441 +#: views.py:448  msgid "Temporary error. Renew your message later."  msgstr "Erreur temporaire. Réenvoyez votre message plus tard." -#: views.py:572 +#: views.py:579  msgid "No category available in this area."  msgstr "Pas de catégorie disponible sur cette zone." -#: views.py:679 +#: views.py:708  msgid "Incorrect choice in the list"  msgstr "Choix incorrect dans la liste" -#: widgets.py:187 +#: widgets.py:240  msgid "Latitude"  msgstr "Latitude" -#: widgets.py:187 +#: widgets.py:240  msgid "Longitude"  msgstr "Longitude" -#: widgets.py:212 +#: widgets.py:265  msgid "Invalid point"  msgstr "Point invalide" -#: widgets.py:262 +#: widgets.py:315  msgid "Creation mode"  msgstr "Mode création" -#: widgets.py:263 +#: widgets.py:316  msgid "To start drawing the route click on the toggle button: \"Draw\"."  msgstr ""  "Pour commencer le dessin cliquez sur le bouton : « Tracer »." -#: widgets.py:265 +#: widgets.py:318  msgid "Then click on the map to begin the drawing."  msgstr "Puis cliquez sur la carte pour commencer le dessin." -#: widgets.py:266 +#: widgets.py:319  msgid "You can add points by clicking again."  msgstr "Vous pouvez ajouter des points en cliquant de nouveau." -#: widgets.py:267 +#: widgets.py:320  msgid ""  "To finish the drawing double click. When the drawing is finished you can "  "edit it." @@ -700,7 +712,7 @@ msgstr ""  "Pour finir le tracé double-cliquez. Quand le tracé est fini vous pouvez "  "toujours l'éditer." -#: widgets.py:269 +#: widgets.py:322  msgid ""  "While creating to undo a drawing click again on the toggle button \"Stop "  "drawing\"." @@ -708,17 +720,17 @@ msgstr ""  "En mode création vous pouvez annuler un tracé en appuyant sur le bouton "  "« Arrêter le tracé »." -#: widgets.py:274 +#: widgets.py:327  msgid "Modification mode"  msgstr "Mode modification" -#: widgets.py:275 +#: widgets.py:328  msgid "To move a point click on it and drag it to the desired position."  msgstr ""  "Pour bouger un point, cliquez dessus, maintenez le click pour le déposer à "  "la position désirée." -#: widgets.py:276 +#: widgets.py:329  msgid ""  "To delete a point move the mouse cursor over it and press the \"d\" or \"Del"  "\" key." @@ -726,7 +738,7 @@ msgstr ""  "Pour supprimer un point, mettez le curseur de la souris sur celui-ci et "  "appuyez sur le touche « d » ou « Suppr »." -#: widgets.py:278 +#: widgets.py:331  msgid ""  "To add a point click in the middle of a segment and drag the new point to "  "the desired position" @@ -735,33 +747,33 @@ msgstr ""  "maintenez le bouton appuyé et déplacez le nouveau point à la position "  "désirée." -#: widgets.py:285 +#: widgets.py:338  msgid "Give a name and set category before uploading a file."  msgstr ""  "Renseignez le nom et choisissez au moins une catégorie avant de déposer un "  "fichier." -#: widgets.py:288 +#: widgets.py:341  msgid "Upload a route file (GPX or KML)"  msgstr "Déposer un trajet (fichier GPX ou KML)" -#: widgets.py:289 +#: widgets.py:342  msgid "or"  msgstr "ou" -#: widgets.py:294 +#: widgets.py:347  msgid "Start \"hand\" drawing"  msgstr "Commencer le tracé manuellement" -#: widgets.py:317 +#: widgets.py:370  msgid "Move on the map"  msgstr "Se déplacer" -#: widgets.py:317 +#: widgets.py:370  msgid "Draw"  msgstr "Tracer" -#: widgets.py:442 +#: widgets.py:495  msgid "Select..."  msgstr "Sélectionner..." @@ -913,6 +925,38 @@ msgstr "Ce site utilise Chimère"  msgid "Map"  msgstr "Carte" +#: templates/chimere/blocks/map_menu.html:5 +msgctxt "routing" +msgid "From" +msgstr "En partir" + +#: templates/chimere/blocks/map_menu.html:6 +msgctxt "routing" +msgid "Add a step" +msgstr "Ajout d'une étape" + +#: templates/chimere/blocks/map_menu.html:7 +msgctxt "routing" +msgid "To" +msgstr "Y aller" + +#: templates/chimere/blocks/map_menu.html:8 +msgctxt "routing" +msgid "Clear the itinerary" +msgstr "Effacer l'itinéraire" + +#: templates/chimere/blocks/map_menu.html:10 +msgid "Zoom in" +msgstr "Zoomer en avant" + +#: templates/chimere/blocks/map_menu.html:11 +msgid "Zoom out" +msgstr "Zoomer en arrière" + +#: templates/chimere/blocks/map_menu.html:12 +msgid "Center the map here" +msgstr "Centrer la carte ici" +  #: templates/chimere/blocks/map_params.html:6  msgid "Permalink"  msgstr "Lien permanent" @@ -923,6 +967,27 @@ msgstr ""  "Utilisez un navigateur internet plus récent ou installez le greffon non "  "libre Flash." +#: templates/chimere/blocks/routing.html:4 +#: templates/chimere/blocks/routing.html:40 +msgid "Itinerary" +msgstr "Itinéraire" + +#: templates/chimere/blocks/routing.html:19 +msgid "Modify" +msgstr "Modifier" + +#: templates/chimere/blocks/routing.html:22 +msgid "New search" +msgstr "Nouvelle recherche" + +#: templates/chimere/blocks/routing.html:27 +msgid "Start:" +msgstr "Départ :" + +#: templates/chimere/blocks/routing.html:31 +msgid "Finish:" +msgstr "Arrivée :" +  #: templates/chimere/blocks/submited.html:3  msgid ""  "Your new proposition/modification has been submited. A moderator will treat " @@ -987,26 +1052,5 @@ msgstr "Choisir une zone pré-définie"  msgid "Or select the area by zooming and panning this map"  msgstr "Ou sélectionner une zone en zoomant et en se déplaçant sur cette carte" -#~ msgid "Submit a modification" -#~ msgstr "Proposer une modification" - -#~ msgid "Add/modify a site" -#~ msgstr "Ajouter ou modifier un site" - -#~ msgid "Categorys" -#~ msgstr "Catégories" - -#~ msgid "Theme" -#~ msgstr "Thème" - -#~ msgid "Subtheme" -#~ msgstr "Sous-thème" - -#~ msgid "Subthemes" -#~ msgstr "Sous-thèmes" - -#~ msgid "Themes" -#~ msgstr "Thèmes" - -#~ msgid "Site name" -#~ msgstr "Nom du site" +#~ msgid "End" +#~ msgstr "Fin" diff --git a/chimere/route.py b/chimere/route.py new file mode 100644 index 0000000..bc08a39 --- /dev/null +++ b/chimere/route.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012  É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. + +""" +Routing management +""" + +import os, re, shutil, tempfile +from BeautifulSoup import BeautifulSoup +from subprocess import Popen, PIPE +from django.contrib.gis.gdal import DataSource + +from django.conf import settings + +class Router: +    def route(self, lon1, lat1, lon2, lat2, transport='foot'): +        ''' +        Get a list of geojson polylines +        ''' +        return [] + +class RoutinoRouter(Router): +    re_desc = [re.compile("<tr class='n'>"), re.compile("<tr class='s'>"), +               re.compile("<tr class='t'>")] +    def route(self, lon1, lat1, lon2, lat2, steps=[], transport='foot'): +        ''' +        Get a list of geojson polylines and route description +        ''' +        language = settings.LANGUAGE_CODE.split('-')[0] +        args = [settings.CHIMERE_ROUTING_ENGINE['PATH'], +                "--dir=%s" % settings.CHIMERE_ROUTING_ENGINE['DB_PATH'], +                "--transport=%s" % transport, +                "--language=%s" % language, +                "--shortest", +                "--output-html", +                "--output-gpx-track", +                "--lat1=%0.15f" % lat1, +                "--lon1=%0.15f" % lon1, +                ] +        lonlat_index = 1 +        for lon, lat in steps: +            lonlat_index += 1 +            args += ["--lat%d=%0.15f" % (lonlat_index, lat), +                     "--lon%d=%0.15f" % (lonlat_index, lon)] +        lonlat_index += 1 +        args += ["--lat%d=%0.15f" % (lonlat_index, lat2), +                 "--lon%d=%0.15f" % (lonlat_index, lon2)] +        tmp_dir = tempfile.mkdtemp(prefix='chimere_') + os.sep +        p = Popen(args, stdout=PIPE, cwd=tmp_dir) +        p.communicate() +        ds = DataSource(tmp_dir + 'shortest-track.gpx') +        if not ds: +            return [], None +        layer = ds[0] +        trk_layer = None +        for layer in ds: +            if layer.name == 'tracks': +                trk_layer = layer +                break +        multilines = trk_layer.get_geoms() +        res = [] +        for multiline in multilines: +            res += [geom.geojson for geom in multiline] +        desc = [] +        # only keeping interessant lines of the desc +        for line in open(tmp_dir + 'shortest.html').readlines(): +            if [True for r in self.re_desc if r.match(line)]: +                desc.append(BeautifulSoup(line).prettify()) +        desc = ['<table>'] + desc[1:-2] + [desc[-1], '</table>'] +        desc = BeautifulSoup('\n'.join(desc)).prettify() +        shutil.rmtree(tmp_dir) +        return res, desc +router = None +if settings.CHIMERE_ROUTING_ENGINE['ENGINE'] == 'routino': +    router = RoutinoRouter() + diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css index f8ed3fe..188cc3b 100644 --- a/chimere/static/chimere/css/styles.css +++ b/chimere/static/chimere/css/styles.css @@ -15,7 +15,8 @@ a, a:link, a:visited, legend,  h2, h3, th, .action li, .action li a,  .action li li a, #no-js-message, -#footer a, #footer a:link, #footer a:visited, .ui-widget-header{ +#footer a, #footer a:link, #footer a:visited, .ui-widget-header, +#chimere_itinerary td.l{      color:#fff;  } @@ -29,7 +30,10 @@ h2, h3, th, .action li, .action li a,  body, h2, h3, th,  .ui-widget-header, -.action li.selected, #no-js-message{ +.action li.selected, #no-js-message, +#content .olControlLayerSwitcher .layersDiv, +#content .olControlLayerSwitcher span, +#chimere_itinerary td.l{      background-color:#449506;  } @@ -38,7 +42,7 @@ body, h2, h3, th,  }  fieldset, .action li, #content, -#map-footer, #panel, #areas, +#map-footer, #panel, #chimere_itinerary_panel, #areas,  #welcome, #detail, .detail_footer a,  #content .olControlLayerSwitcher .layersDiv,  #content .olControlLayerSwitcher span, @@ -63,7 +67,7 @@ div.warning,  #content,  .action li.selected,  #content .olControlLayerSwitcher .layersDiv, -#panel, #map-footer, +#panel, #map-footer, #chimere_itinerary_panel,  #utils-div{      border:1px solid #327e04;  } @@ -79,7 +83,7 @@ div.warning,      opacity:0.9;  } -#panel, #areas, #detail, #category_detail{ +#panel, #areas, #detail, #category_detail, #chimere_itinerary_panel{      opacity:0.8;  } @@ -409,12 +413,111 @@ ul#share li{      top:50px;      right:18px;      width:300px; -    bottom:44px; +    max-height:300px;      overflow:auto;      padding:0.5em;      padding-top:0;  } +#chimere_itinerary_panel, +#chimere_itinerary{ +    display:none; +} + +#chimere_itinerary_panel label{ +    color:#000; +} + +#chimere_itinerary_panel p +{ +    margin:0.5em; +} + +.itinerary_label{ +    font-size:0.9em; +    padding-top:0.5em; +     font-style:italic; +} + +.itinerary_label.label{ +  font-style:normal; +  font-weight:bold; +} + + +#chimere_itinerary_content{ +    overflow:auto; +    height:190px; +    margin-top:10px; +} + +#chimere_itinerary_content table +{ +    border-collapse:collapse; +} + +#chimere_itinerary_content table td{ +    border:1px solid #333; +} + +#chimere_itinerary_content td.l{ +    padding:5px; +    width:60px; +} + +#chimere_itinerary_content td.r{ +    font-size:0.8em; +    padding:0.8em; +} + +#chimere_itinerary_content span.j{ +    font-style:italic; +} + +#chimere_itinerary_content span.t, +#chimere_itinerary_content span.b +{ +    text-transform: lowercase; +} + +#chimere_map_menu{ +    z-index:4; +    display:none; +    position:absolute; +    padding:0.5em; +    background-color:#fff; +    border:1px solid #bbb; +    -webkit-border-radius: 0 8px 8px 8px; +    -moz-border-radius: 0 8px 8px 8px; +    border-radius: 0 8px 8px 8px; +} + +#map_menu_clear{ +    display:none; +} + +#map_menu_zoomin{ +    border-top:1px solid #999; +} + +#chimere_map_menu ul, #chimere_map_menu li{ +    padding:0.2em; +    margin:0; +    list-style:none; +} + +#chimere_map_menu li:hover{ +    cursor:pointer; +    background-color:#ccc; +} + +.nominatim-label{ +    display:block; +    font-size:0.9em; +    font-weight:bold; +    height:2.8em; +} +  .simple #panel{      top:5px;  } @@ -533,26 +636,18 @@ p.warning{  } -#welcome_button, +a#welcome_button, +a#routing_button,  #permalink{      display: block; -    text-align:center;      margin:0.3em;      padding:0.2em; -} - -a#welcome_button, -#permalink{ +    width:100%;      font-size:14px;      text-align:center;      text-decoration:none;  } -#welcome_button, -#permalink{ -    width:100%; -} -  /* forms */  table.inline-table{ diff --git a/chimere/static/chimere/img/flag-finish.png b/chimere/static/chimere/img/flag-finish.pngBinary files differ new file mode 100644 index 0000000..04bfa1d --- /dev/null +++ b/chimere/static/chimere/img/flag-finish.png diff --git a/chimere/static/chimere/img/flag-start.png b/chimere/static/chimere/img/flag-start.pngBinary files differ new file mode 100644 index 0000000..c93f2a3 --- /dev/null +++ b/chimere/static/chimere/img/flag-start.png diff --git a/chimere/static/chimere/img/flag-step.png b/chimere/static/chimere/img/flag-step.pngBinary files differ new file mode 100644 index 0000000..5556c94 --- /dev/null +++ b/chimere/static/chimere/img/flag-step.png diff --git a/chimere/static/chimere/img/images_licences b/chimere/static/chimere/img/images_licences index 0e732fc..cbc9307 100644 --- a/chimere/static/chimere/img/images_licences +++ b/chimere/static/chimere/img/images_licences @@ -1,5 +1,5 @@ -* Upload image credit +* Upload image credit (upload.png)      * Farm-Fresh layer gps.png in Farm-Fresh Web Icons  Author: FatCow Web Hosting @@ -16,7 +16,7 @@ Author: The Tango! Desktop Project  Licence: Public domain  Url: http://commons.wikimedia.org/wiki/File:Internet-web-browser.svg -* Drawing image credit +* Drawing image credit (drawing.png)      * Icons from the Tango! project set.  Author: The Tango! Desktop Project @@ -24,9 +24,17 @@ Licence: Public domain  Url: http://commons.wikimedia.org/wiki/File:Edit-find-replace.svg  Url 2: http://commons.wikimedia.org/wiki/File:Internet-web-browser.svg -* Quaver image credit +* Quaver image credit (8thNote.png)      *   An 8th-note.  Author: Sbrools  Licence: Public domain  Url: https://commons.wikimedia.org/wiki/File:8thNote.svg + +* Flags image credit (flag-start.png, flag-step.png, flag-finish.png) + +Author: FatCow Web Hosting +Licence: Creative Commons Attribution 3.0 United States license +Url: https://upload.wikimedia.org/wikipedia/commons/c/cb/Farm-Fresh_flag_1.png +     https://upload.wikimedia.org/wikipedia/commons/8/81/Farm-Fresh_flag_blue.png +     https://upload.wikimedia.org/wikipedia/commons/6/64/Farm-Fresh_flag_finish.png diff --git a/chimere/static/chimere/js/jquery.chimere.js b/chimere/static/chimere/js/jquery.chimere.js index fbb3ecd..c1d028c 100644 --- a/chimere/static/chimere/js/jquery.chimere.js +++ b/chimere/static/chimere/js/jquery.chimere.js @@ -77,10 +77,15 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              controls:[new OpenLayers.Control.Navigation(),                        new OpenLayers.Control.SimplePanZoom(),                        new OpenLayers.Control.ScaleLine()], +            category_accordion: true, // category opening behave like an accordion              maxResolution: 156543.0399,              units: 'm',              projection: new OpenLayers.Projection('EPSG:4326'),              theme:null, +            routing: false, // enable routing management +            routing_panel_open: function(){ +                                    $('#chimere_itinerary_panel').dialog('open'); +                                },              current_feature: null, // To store the active POI              current_control: null, // To store the current control              current_popup: null, // To store the current POI popup displayed @@ -119,6 +124,8 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  map_options['restrictedExtent'] = settings.restricted_extent;              } +            settings.current_position = null; +              /* Create map object */              settings.map = map = new OpenLayers.Map(map_element, map_options); @@ -168,6 +175,40 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              }              settings.map.setBaseLayer(                              settings.map_layers[settings.selected_map_layer]); + +            /* manage the context menu  */ +            $('#map_menu_zoomin').bind("click", methods.zoomIn); +            $('#map_menu_zoomout').bind("click", methods.zoomOut); +            $('#map_menu_center').bind("click", methods.mapCenter); +            /* manage the routing */ +            if (settings.routing){ +                settings.routing_start = null; +                settings.routing_steps = new Array(); +                settings.routing_end = null; +                settings.icon_start = new OpenLayers.Icon( +                    STATIC_URL + "chimere/img/flag-start.png", +                    new OpenLayers.Size(32, 32), +                    new OpenLayers.Pixel(0, -32)); +                settings.icon_step = new OpenLayers.Icon( +                    STATIC_URL + "chimere/img/flag-step.png", +                    new OpenLayers.Size(32, 32), +                    new OpenLayers.Pixel(0, -32)); +                settings.icon_end = new OpenLayers.Icon( +                    STATIC_URL + "chimere/img/flag-finish.png", +                    new OpenLayers.Size(32, 32), +                    new OpenLayers.Pixel(0, -32)); +                $('#map_menu_from').bind("click", methods.routingFrom); +                $('#map_menu_step').bind("click", methods.routingAddStep); +                $('#map_menu_to').bind("click", methods.routingTo); +                $('#map_menu_clear').bind("click", methods.routingClear); +                settings.layerRoute = new OpenLayers.Layer.Vector("Route Layer"); +                settings.map.addLayer(settings.layerRoute); +                settings.layerRoute.setOpacity(0.8); +                settings.layerRouteMarker = new OpenLayers.Layer.Markers( +                                                               'Route markers'); +                settings.map.addLayer(settings.layerRouteMarker); +                settings.layerRouteMarker.setOpacity(0.8); +            }              /* Vectors layer */              settings.layerVectors = new OpenLayers.Layer.Vector("Vector Layer");              settings.map.addLayer(settings.layerVectors); @@ -211,7 +252,7 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  methods.loadGeoObjects();                  // Hide popUp when clicking on map                  settings.map.events.register('click', settings.map, -                                             methods.hidePopup); +                                             methods.displayMapMenu);              } else {                  if (!settings.edition_type_is_route){                      map.events.register('click', settings.map, @@ -225,6 +266,43 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              }          }, // end of init +        // zoom in from the map menu +        zoomIn: function(){ +            methods.mapCenter(); +            settings.map.zoomIn(); +        }, + +        // zoom out from the map menu +        zoomOut: function(){ +            methods.mapCenter(); +            settings.map.zoomOut(); +        }, + +        // center from the map menu +        mapCenter: function(){ +            $('#chimere_map_menu').hide(); +            settings.map.setCenter(settings.current_position); +        }, + +        /* +        * Display menu on the map +        */ +        displayMapMenu: function(e) { +            if (methods.hidePopup()) return; +            if ($('#chimere_map_menu').is(":visible")){ +                $('#chimere_map_menu').hide(); +            } else{ +                settings.current_position = +                                settings.map.getLonLatFromViewPortPx(e.xy); +                var offsetX = e.pageX; +                var offsetY = e.pageY; +                $('#chimere_map_menu').show('fast'); +                $('#chimere_map_menu').css('display', 'block'); +                $('#chimere_map_menu').css('top', offsetY); +                $('#chimere_map_menu').css('left', offsetX); +            } +        }, +          /*          * Load markers and route from DB          */ @@ -289,7 +367,13 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  category_element.parent().find("li input").attr("checked", val);              }              var _toggle_categories = function (subcategory_element) { -                var parent = subcategory_element.parent().parent().parent(); +                var parent = subcategory_element.closest('ul'); +                var parent_label = parent.parent().find("> span > label"); +                if (parent.find('input[type=checkbox]:checked').length){ +                    parent_label.addClass('category-selected'); +                } else { +                    parent_label.removeClass('category-selected'); +                }                  var master_check = parent.find("> input");                  if (parent.find('.subcategories input[type=checkbox]').length ==                      parent.find('.subcategories input[type=checkbox]:checked').length){ @@ -297,6 +381,14 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  } else {                      master_check.removeAttr('checked');                  } + +                if($('#action-categories').length){ +                    if ($('#categories input[type=checkbox]:checked').length){ +                        $('#action-categories').addClass('category-selected'); +                    } else { +                        $('#action-categories').removeClass('category-selected'); +                    } +                }                  return master_check;              };              var _init_categories = function () { @@ -383,12 +475,12 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  }                  else                  { +                    $('#chimere_map_menu').hide();                      // Default popup                      if (feature.popup && feature.popup.visible()) {                          if (settings.current_popup == feature.popup) {                              feature.popup.hide();                              if (!settings.simple){ -                                $('#panel').removeClass('panel-minified');                                  $('#detail').hide();                              }                          } else { @@ -448,6 +540,164 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              settings.current_feature.geometry = linestring;              settings.layerVectors.addFeatures([settings.current_feature]);          }, +        routingInputChange: function(){ +            $('#map_menu_clear').show(); +            if ($('#nominatim_start_lon').val()){ +                settings.routing_start = new OpenLayers.Marker( +                  new OpenLayers.LonLat($('#nominatim_start_lon').val(), +                                        $('#nominatim_start_lat').val() +                                        ).transform(EPSG_DISPLAY_PROJECTION, +                                            settings.map.getProjectionObject()), +                                      settings.icon_start); +                settings.layerRouteMarker.addMarker(settings.routing_start); +            } +            if ($('#nominatim_end_lon').val()){ +                settings.routing_end = new OpenLayers.Marker( +                  new OpenLayers.LonLat($('#nominatim_end_lon').val(), +                                        $('#nominatim_end_lat').val() +                                        ).transform(EPSG_DISPLAY_PROJECTION, +                                            settings.map.getProjectionObject()), +                                      settings.icon_end); +                settings.layerRouteMarker.addMarker(settings.routing_end); +            } +            if (settings.routing_end && settings.routing_start) methods.route(); +        }, + +        // set the start point for routing +        routingFrom: function(){ +            $('#chimere_map_menu').hide(); +            settings.routing_panel_open(); +            $('#map_menu_clear').show(); +            settings.routing_start = new OpenLayers.Marker( +                                          settings.current_position.clone(), +                                          settings.icon_start); +            settings.layerRouteMarker.addMarker(settings.routing_start); +            if (nominatim_url){ +                helpers.updateNominatimName(settings.current_position.clone() +                                 .transform(settings.map.getProjectionObject(), +                                            EPSG_DISPLAY_PROJECTION), +                                            'start_label'); +            } +            if (settings.routing_end) methods.route(); +        }, +        // add a step point for routing +        routingAddStep: function(){ +            $('#chimere_map_menu').hide(); +            settings.routing_panel_open(); +            $('#map_menu_clear').show(); +            settings.routing_steps.push(new OpenLayers.Marker( +                                          settings.current_position.clone(), +                                          settings.icon_step.clone())); +            settings.layerRouteMarker.addMarker( +                       settings.routing_steps[settings.routing_steps.length-1]); +            if (settings.routing_end && settings.routing_start) methods.route(); +        }, + +        // set the finish point for routing +        routingTo: function(){ +            $('#chimere_map_menu').hide(); +            settings.routing_panel_open(); +            $('#map_menu_clear').show(); +            settings.routing_end = new OpenLayers.Marker( +                                          settings.current_position.clone(), +                                          settings.icon_end); +            settings.layerRouteMarker.addMarker(settings.routing_end); +            if (nominatim_url){ +                helpers.updateNominatimName(settings.current_position.clone() +                                 .transform(settings.map.getProjectionObject(), +                                            EPSG_DISPLAY_PROJECTION), +                                            'end_label'); +            } +            if (settings.routing_start) methods.route(); +        }, + +        // clear the current itinerary +        routingClear: function(){ +            $('#nominatim_start_lon').val(''); +            $('#nominatim_start_lat').val(''); +            $('#nominatim_start_label').html(''); +            $('#chimere_start_label').html(''); +            $('#nominatim_end_lon').val(''); +            $('#nominatim_end_lat').val(''); +            $('#nominatim_end_label').html(''); +            $('#chimere_end_label').html(''); +            $('.nominatim-widget').val(''); +            $('#chimere_map_menu').hide(); +            $('#map_menu_clear').hide(); +            $('#chimere_itinerary').hide(); +            $('#chimere_itinerary_form').show(); +            settings.layerRoute.removeAllFeatures(); +            settings.layerRouteMarker.clearMarkers(); +            settings.routing_start = null; +            settings.routing_end = null; +            settings.routing_steps = new Array(); +        }, + +        // display a route +        route: function(){ +            if (!settings.routing_start || !settings.routing_end){ +                return; +            } +            var steps = [settings.routing_start.lonlat.clone()] +            for (var i = 0; i < settings.routing_steps.length; i++) { +                steps.push(settings.routing_steps[i].lonlat.clone()); +            } +            steps.push(settings.routing_end.lonlat.clone()); +            // create the appropriate URL +            var uri = extra_url + "route/" +            var transport = $('input:radio[name=transport]:checked').val(); +            uri += transport + "/" +            for (var i = 0; i < steps.length; i++) { +                var step = steps[i].transform( +                                            settings.map.getProjectionObject(), +                                            EPSG_DISPLAY_PROJECTION); +                if (i > 0){ +                    uri += '_'; +                } +                uri += step.lon + '_' + step.lat; +            } +            $.ajax({url: uri,  +                    dataType: "json", +                    success: function (data) { +                        settings.layerRoute.removeAllFeatures(); +                        for (var i = 0; i < data.features.length; i++) { +                            methods.putRoute(data.features[i]); +                        } +                        settings.map.zoomToExtent( +                                settings.layerRoute.getDataExtent()); +                        settings.map.zoomOut(); +                        $('#chimere_itinerary_content').html( +                                            data.properties.description); +                        $('#chimere_itinerary').show(); +                        $('#chimere_itinerary_form').hide(); +                        settings.routing_panel_open(); +                    }, +                    error: function (data) { +                        settings.layerRoute.removeAllFeatures(); +                    } +                }); + +        }, +        /* +        Put a route on the map +        */ +        putRoute: function(polyline) { +            var point_array = new Array(); +            for (i=0; i<polyline.coordinates.length; i++){ +                var point = new OpenLayers.Geometry.Point(polyline.coordinates[i][0], +                                                          polyline.coordinates[i][1]); +                point_array.push(point); +            } +            var linestring = new OpenLayers.Geometry.LineString(point_array); +            linestring.transform(EPSG_DISPLAY_PROJECTION, settings.map.getProjectionObject()); +            current_route = new OpenLayers.Feature.Vector(); +            var style = OpenLayers.Util.extend({}, +                                     OpenLayers.Feature.Vector.style['default']); +            style.strokeWidth = 3; +            current_route.style = style; +            current_route.geometry = linestring; +            settings.layerRoute.addFeatures([current_route]); +        },          display_feature_detail: function (pk) {              /*              * update current detail panel with an AJAX request @@ -466,7 +716,6 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                              }                              else {                                  if (!settings.simple) { -                                    $('#panel').addClass('panel-minified');                                      $('#detail').html(data).show();                                  }                                  else { @@ -508,9 +757,11 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              // Check if element is currently visible or not              var was_visible = $("#maincategory_" + id).is(":visible");              // Close all categories -            $("#categories ul.subcategories").hide(); +            if (settings.category_accordion){ +                $("#categories ul.subcategories").hide(); +                $("#categories img.toggle_category").attr("src", STATIC_URL + "chimere/img/plus.png"); +            }              // Put a minus image -            $("#categories img.toggle_category").attr("src", STATIC_URL + "chimere/img/plus.png");              if (!was_visible)              {                  // Show the subcategories @@ -519,6 +770,12 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                  $("#maincategory_img_" + id).attr("src", STATIC_URL + "chimere/img/minus.png");                  settings.current_category = id;              } +            if (!settings.category_accordion && was_visible) +            { +                $("#maincategory_" + id).toggle(); +                // Put a minus image +                $("#maincategory_img_" + id).attr("src", STATIC_URL + "chimere/img/plus.png"); +            }          },          zoomToCurrentExtent: function(){              /* zoom to current extent */ @@ -629,13 +886,16 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              else { // Default behaviour                  if (settings.current_popup)                  { -                    settings.current_popup.hide();                      if (!settings.simple){ -                        $('#panel').removeClass('panel-minified');                          $('#detail').hide();                      } +                    if (settings.current_popup.visible()){ +                        settings.current_popup.hide(); +                        return true; +                    }                  }              } +            return false;          },          saveExtent: function(){              var extent_key = 'MAP_EXTENT'; @@ -770,8 +1030,22 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {              if (vertices){                  jQuery('#id_point').val(vertices);              } +        }, +        updateNominatimName:function(lonlat, response_id){ +            $.ajax({ +                url: nominatim_url.substring(0, nominatim_url.length-6) + 'reverse', +                data: { +                    format: "json", +                    lat:lonlat.lat, +                    lon:lonlat.lon +                }, +                success: function (data) { +                    vals = $.parseJSON(data); +                    $('#nominatim_'+response_id).html(vals.display_name); +                    $('#chimere_'+response_id).html(vals.display_name); +                } +            });          } -      }; // End of helpers      $.fn.chimere = function (thing) { diff --git a/chimere/static/chimere/js/nominatim-widget.js b/chimere/static/chimere/js/nominatim-widget.js new file mode 100644 index 0000000..fea654d --- /dev/null +++ b/chimere/static/chimere/js/nominatim-widget.js @@ -0,0 +1,41 @@ + +$(function(){ +    $(".nominatim-widget").autocomplete({ +           source: function (request, response) { +               $.ajax({ +                      url: nominatim_url, +                      data: { +                          format: "json", +                          q: request.term, +                      }, +                      success: function ( data ) { +                          response ( $.map( $.parseJSON(data), function( item ) { +                              return { +                                  label: item.display_name, +                                  value: item.display_name, +                                  lat: item.lat, +                                  lon: item.lon +                              }})); + +                          } +                      }) +            }, +            minLength: 6, +            delay: 1000, +            select: function ( event, ui ) { +                $('#'+$(this).attr('id')+'_lat').val(ui.item.lat); +                $('#'+$(this).attr('id')+'_lon').val(ui.item.lon); +                $('#'+$(this).attr('id')+'_label').html(ui.item.label); +                $('#chimere_'+$(this).attr('id').substring(10)+'_label').html(ui.item.label); +                $('#'+$(this).attr('id')).val(''); +                jQuery("#map").chimere("routingInputChange"); +                return false; +            }, +            open: function() { +                $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); +            }, +            close: function() { +                $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); +            } +    }); +}); diff --git a/chimere/templates/base.html b/chimere/templates/base.html index 2a50fc3..149cc71 100644 --- a/chimere/templates/base.html +++ b/chimere/templates/base.html @@ -7,25 +7,25 @@      {% endblock %}  </head>  <body {% block body_id %}{% endblock %} {% block body_class %}{% endblock %}> -    {% block extrabody %} +{% block extrabody %} +{% endblock %} +{% block body %} +<div id="header"> +    {% block header %}      {% endblock %} -    {% block body %} -        <div id="header"> -            {% block header %} -            {% endblock %} -        </div> -        <div id="sidebar"> -            {% block sidebar %} -            {% endblock %} -        </div> -        <div id="content"> -            {% block content %} -            {% endblock %} -        </div> -        <div id="footer"> -            {% block footer %} -            {% endblock %} -        </div> +</div> +<div id="sidebar"> +    {% block sidebar %}      {% endblock %} +</div> +<div id="content"> +    {% block content %} +    {% endblock %} +</div> +<div id="footer"> +    {% block footer %} +    {% endblock %} +</div> +{% endblock %}  </body>  </html> diff --git a/chimere/templates/chimere/blocks/categories.html b/chimere/templates/chimere/blocks/categories.html index 5ba89fc..9c5ffd6 100644 --- a/chimere/templates/chimere/blocks/categories.html +++ b/chimere/templates/chimere/blocks/categories.html @@ -1,29 +1,29 @@  {% load i18n %} -<ul id='ul_categories'> -    {% for category, lst_sub_categories in sub_categories %} -    <li> -        <img class="control_image toggle_category" id="maincategory_img_{{category.id}}" alt="control" src="{{ STATIC_URL }}chimere/img/{% if category.selected %}minus.png{% else %}plus.png{% endif %}" /> -        <input type="checkbox" id='checkall_{{category.id}}'> -        {% trans category.name %} -        <img id="zoom_to_category_{{category.id}}" class="zoom_image zoom_to_category" alt='{% trans "Zoom to" %} {{category.name}}' src='{{ STATIC_URL }}chimere/img/zoom.png' /> -        <ul class='subcategories' id='maincategory_{{category.id}}'{% if not category.selected %} style='display:None'{% endif %}> -            {% for sub_category in lst_sub_categories %} -            <li id='li_sub_{{sub_category.id}}'> -                <input type='checkbox' name='category_{{sub_category.id}}' id='category_{{sub_category.id}}'{% if sub_category.selected %} checked='checked'{% endif %}/> -                <label for='category_{{sub_category.id}}'> -                    <img alt='{{ sub_category.name }}' src='{{ MEDIA_URL }}{{sub_category.icon.image}}'/> -                    {% trans sub_category.name %} -                </label> -                <img id="zoom_to_subcategory_{{sub_category.id}}" class="zoom_image zoom_to_subcategory" alt='{% trans "Zoom to" %} {{sub_category.name}}' src='{{ STATIC_URL }}chimere/img/zoom.png' /> -            </li> -            {% endfor %} -            {% if category.description %} -                <li><a href="#" onclick="$('#map').chimere('category_detail', {{category.id}});">{% trans "Tell me more..." %}</a></li> -            {% endif %} -        </ul> -    </li> -    {% endfor %} -    <li id='display_submited'> -        <input type='checkbox' name='display_submited' id='display_submited_check'/> {% trans "Display markers and routes waiting for validation"%} -    </li> -</ul> +    <ul id='ul_categories'> +        {% for category, lst_sub_categories in sub_categories %} +        <li> +            <img class="control_image toggle_category" id="maincategory_img_{{category.id}}" alt="control" src="{{ STATIC_URL }}chimere/img/{% if category.selected %}minus.png{% else %}plus.png{% endif %}" /> +            <input type="checkbox" id='checkall_{{category.id}}'> +            {% trans category.name %} +            <img id="zoom_to_category_{{category.id}}" class="zoom_image zoom_to_category" alt='{% trans "Zoom to" %} {{category.name}}' src='{{ STATIC_URL }}chimere/img/zoom.png' /> +            <ul class='subcategories' id='maincategory_{{category.id}}'{% if not category.selected %} style='display:None'{% endif %}> +                {% for sub_category in lst_sub_categories %} +                <li id='li_sub_{{sub_category.id}}'> +                    <input type='checkbox' name='category_{{sub_category.id}}' id='category_{{sub_category.id}}'{% if sub_category.selected %} checked='checked'{% endif %}/> +                    <label for='category_{{sub_category.id}}'> +                        <img alt='{{ sub_category.name }}' src='{{ MEDIA_URL }}{{sub_category.icon.image}}'/> +                        {% trans sub_category.name %} +                    </label> +                    <img id="zoom_to_subcategory_{{sub_category.id}}" class="zoom_image zoom_to_subcategory" alt='{% trans "Zoom to" %} {{sub_category.name}}' src='{{ STATIC_URL }}chimere/img/zoom.png' /> +                </li> +                {% endfor %} +                {% if category.description %} +                    <li><a href="#" onclick="$('#map').chimere('category_detail', {{category.id}});">{% trans "Tell me more..." %}</a></li> +                {% endif %} +            </ul> +        </li> +        {% endfor %} +        <li id='display_submited'> +            <input type='checkbox' name='display_submited' id='display_submited_check'/> {% trans "Display markers and routes waiting for validation"%} +        </li> +    </ul> diff --git a/chimere/templates/chimere/blocks/map_menu.html b/chimere/templates/chimere/blocks/map_menu.html new file mode 100644 index 0000000..38fb4a8 --- /dev/null +++ b/chimere/templates/chimere/blocks/map_menu.html @@ -0,0 +1,14 @@ +{% load i18n %} +<div id='chimere_map_menu'> +    <ul> +        {% if routing %} +        <li id='map_menu_from' class='routing_item'>{% trans "From" context "routing" %}</li> +        <li id='map_menu_step' class='routing_item'>{% trans "Add a step" context "routing" %}</li> +        <li id='map_menu_to' class='routing_item'>{% trans "To" context "routing" %}</li> +        <li id='map_menu_clear' class='routing_item'>{% trans "Clear the itinerary" context "routing" %}</li> +        {% endif%} +        <li id='map_menu_zoomin'>{% trans "Zoom in" %}</li> +        <li id='map_menu_zoomout'>{% trans "Zoom out" %}</li> +        <li id='map_menu_center'>{% trans "Center the map here" %}</li> +    </ul> +</div> diff --git a/chimere/templates/chimere/blocks/map_params.html b/chimere/templates/chimere/blocks/map_params.html index 57ced90..27762a3 100644 --- a/chimere/templates/chimere/blocks/map_params.html +++ b/chimere/templates/chimere/blocks/map_params.html @@ -7,6 +7,7 @@      chimere_init_options["map_layers"] = [{{map_layers|safe|escape}}];      chimere_init_options['permalink_label'] = '{%trans "Permalink"%}';      chimere_init_options['permalink_element'] = document.getElementById('permalink'); +    chimere_init_options['routing'] = {{routing}};      {% if dynamic_categories %}chimere_init_options['dynamic_categories'] = true;{% endif %}      {% if default_area %}      chimere_init_options["default_area"] = new Array({{default_area.upper_left_corner.x}}, {{default_area.upper_left_corner.y}}, {{default_area.lower_right_corner.x}}, {{default_area.lower_right_corner.y}}); diff --git a/chimere/templates/chimere/blocks/routing.html b/chimere/templates/chimere/blocks/routing.html new file mode 100644 index 0000000..4948a1c --- /dev/null +++ b/chimere/templates/chimere/blocks/routing.html @@ -0,0 +1,56 @@ +{% load i18n %} +{% if routing %} +{{itinerary_form.media}} +<a href='#' id='routing_button' class='ui-widget ui-button ui-state-default ui-corner-all'>{% trans "Itinerary"%}</a> +<div id='chimere_itinerary_panel'> +    <div id='chimere_itinerary_form'> +    {% for hidden in itinerary_form.hidden_fields %} +      {{ hidden }} +    {% endfor %} +    {% for field in itinerary_form.visible_fields %} +    {% if field.label %}<p><label for='{{field.auto_id}}'>{{ field.label }}</label></p>{%endif%} +    <p>{{field}}</p> +    {% endfor %} +    </div> +    <div id='chimere_itinerary'> +        <div id='chimere_itinerary_action'> +            <ul class='action'> +                <li class='ui-widget ui-button ui-state-default ui-corner-all'> +                    <a href='#' id='chimere_itinerary_modify'>{% trans "Modify" %}</a> +                </li> +                <li class='ui-widget ui-button ui-state-default ui-corner-all'> +                    <a href='#' id='chimere_itinerary_new'>{% trans "New search" %}</a> +                </li> +            </ul> +        </div> +        <div class='itinerary_label'> +            <span class='label'>{% trans "Start:"%}</span> <span id='chimere_start_label'></span></div> +        <div id='chimere_itinerary_content'> +        </div> +        <div class='itinerary_label'> +            <span class='label'>{% trans "Finish:"%}</span> <span id='chimere_end_label'></span> +        </div> +    </div> +</div> +<script language='javascript' type='text/javascript'> +$(document).ready(function() { +    $('#chimere_itinerary_panel').dialog({ +            autoOpen: false, +            position: [50, 50], +            title: "{% trans "Itinerary" %}", +            height: 360, +            resizable: false +    }); +    $('#routing_button').click(function(){ +        $('#chimere_itinerary_panel').dialog('open'); +    }); +    $('#chimere_itinerary_modify').click(function(){ +        $('#chimere_itinerary').hide(); +        $('#chimere_itinerary_form').show(); +    }); +    $('#chimere_itinerary_new').click(function(){ +        $('#map').chimere('routingClear'); +    }); +}); +</script> +{% endif%} diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html index 5a49e46..4a53b3f 100644 --- a/chimere/templates/chimere/main_map.html +++ b/chimere/templates/chimere/main_map.html @@ -22,6 +22,7 @@      {% if areas_visible %}          {% display_areas %}      {% endif %} +        {% routing %}          {% display_news news_visible %}          <div id='permalink' class='ui-widget ui-button ui-state-default ui-corner-all'></div>      </div> @@ -34,6 +35,7 @@  <script type="text/javascript">     $("#main-map").show();  </script> +    {% map_menu %}      {% map_params %}  {% endblock %}  {% block footer %} diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py index 46fb422..132af09 100644 --- a/chimere/templatetags/chimere_tags.py +++ b/chimere/templatetags/chimere_tags.py @@ -111,6 +111,17 @@ def head_chimere(context):               }      return context_data +@register.inclusion_tag('chimere/blocks/map_menu.html', takes_context=True) +def map_menu(context): +    context_data =  {'routing':settings.CHIMERE_ENABLE_ROUTING} +    return context_data + +@register.inclusion_tag('chimere/blocks/routing.html', takes_context=True) +def routing(context): +    context_data =  {'routing':settings.CHIMERE_ENABLE_ROUTING, +                     'itinerary_form':context['itinerary_form']} +    return context_data +  @register.inclusion_tag('chimere/blocks/map_params.html', takes_context=True)  def map_params(context):      context_data =  {} @@ -118,6 +129,8 @@ def map_params(context):      context_data['icon_offset_y'] = settings.CHIMERE_ICON_OFFSET_Y      context_data['icon_width'] = settings.CHIMERE_ICON_WIDTH      context_data['icon_height'] = settings.CHIMERE_ICON_HEIGHT +    context_data['routing'] = 'true' if settings.CHIMERE_ENABLE_ROUTING \ +                              else 'none'      area_name = context['area_name'] if 'area_name' in context else 'area_name'      map_layers, default_area = get_map_layers(area_name)      context_data['map_layers'] = ", ".join(map_layers) diff --git a/chimere/urls.py b/chimere/urls.py index a232382..81263a0 100644 --- a/chimere/urls.py +++ b/chimere/urls.py @@ -51,6 +51,17 @@ if settings.CHIMERE_FEEDS:              LatestPOIsByZoneID(), name='feeds-areaid'),      ) +if settings.CHIMERE_ENABLE_ROUTING: +    urlpatterns += patterns('chimere.views', +        url(r'^(?P<area_name>[a-zA-Z0-9_-]*/)?route/'\ +            r'(?P<transport>(%s))/' +            r'(?P<lon1>[-]?[0-9]+[.]?[0-9]*)_(?P<lat1>[-]?[0-9]+[.]?[0-9]*)_'\ +            r'(?P<lonlat_steps>([-]?[0-9]+[.]?[0-9]*_[-]?[0-9]+[.]?[0-9]*_)*)'\ +            r'(?P<lon2>[-]?[0-9]+[.]?[0-9]*)_(?P<lat2>[-]?[0-9]+[.]?[0-9]*)$' % +           ('|'.join([key for key, lbl in settings.CHIMERE_ROUTING_TRANSPORT])), +                'route', name="route"), +    ) +  urlpatterns += patterns('chimere.views',      url(r'^charte/?$', 'charte', name="charte"),      url(r'^(?P<area_name>[a-zA-Z0-9_-]+/)?contact/?$', 'contactus', name="contact"), diff --git a/chimere/views.py b/chimere/views.py index f10b7fa..c71b4eb 100644 --- a/chimere/views.py +++ b/chimere/views.py @@ -26,6 +26,7 @@ Views of the project  import datetime  from itertools import groupby +import simplejson  from django.conf import settings  from django.core import serializers @@ -47,7 +48,9 @@ from chimere.widgets import getMapJS, PointChooserWidget, \                              RouteChooserWidget, AreaWidget  from chimere.forms import MarkerForm, RouteForm, ContactForm, FileForm, \       FullFileForm, MultimediaFileFormSet, PictureFileFormSet, notifySubmission,\ -     notifyStaff, AreaForm +     notifyStaff, AreaForm, RoutingForm + +from chimere.route import router  def get_base_uri(request):      base_uri = 'http://' @@ -120,6 +123,8 @@ def index(request, area_name=None, default_area=None, simple=False):      if request.GET and 'lat' in request.GET \        and 'lon' in request.GET:          zoomout = None +    if settings.CHIMERE_ENABLE_ROUTING: +        response_dct['itinerary_form'] = RoutingForm()      response_dct.update({           'actions':actions, 'action_selected':('view',),           'error_message':'', @@ -635,6 +640,28 @@ def redirectFromTinyURN(request, area_name='', tiny_urn=''):          return redir      return HttpResponseRedirect(response_dct['extra_url'] + parameters) +def route(request, area_name, lon1, lat1, lonlat_steps, lon2, lat2, +          transport='foot'): +    ''' +    Get the JSON for a route +    ''' +    try: +        lon1, lat1 = float(lon1), float(lat1) +        lon2, lat2 = float(lon2), float(lat2) +        steps = [float(lonlat) for lonlat in lonlat_steps.split('_') if lonlat] +        # regroup by 2 +        steps = [(steps[i*2], steps[i*2+1]) for i in range(len(steps)/2)] +    except ValueError: +        return HttpResponse('no results') +    jsons, desc = router.route(lon1, lat1, lon2, lat2, steps=steps, +                               transport=transport) +    if not jsons: +        return HttpResponse('no results') +    jsonencoder = simplejson.JSONEncoder() +    data = '{"properties":{"description":%s}, "type": "FeatureCollection",'\ +           '"features":[%s]}' % (jsonencoder.encode(desc), ",".join(jsons)) +    return HttpResponse(data) +  def rss(request, area_name=''):      '''      Redirect to RSS subscription page diff --git a/chimere/widgets.py b/chimere/widgets.py index 8e2b8ad..b520fcd 100644 --- a/chimere/widgets.py +++ b/chimere/widgets.py @@ -26,6 +26,9 @@ from django.core.exceptions import ObjectDoesNotExist  from django.conf import settings  from django.contrib.gis.db import models  from django.contrib.gis.geos import fromstr +from django.utils.html import conditional_escape +from django.forms.widgets import RadioInput, RadioFieldRenderer +from django.utils.encoding import force_unicode  from django.utils.safestring import mark_safe  from django.utils.translation import ugettext as _ @@ -100,6 +103,41 @@ class ChosenSelectWidget(forms.Select):          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 TextareaWidget(forms.Textarea):      """      Manage the edition of a text using TinyMCE @@ -121,6 +159,21 @@ class DatePickerWidget(forms.TextInput):          u"</script>\n" % kwargs['attrs']['id']          return mark_safe(rendered) +class NominatimWidget(forms.TextInput): +    class Media: +        js = ["%schimere/js/nominatim-widget.js" % settings.STATIC_URL] +    def render(self, name, value, attrs=None, area_name=''): +        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'/> +<label class='nominatim-label' id='nominatim_%(id)s_label'> </label> +<script type='text/javascript'> +var nominatim_url = "%(nominatim_url)s"; +</script> +""" % {'id':name, 'nominatim_url':settings.NOMINATIM_URL} +        return mark_safe(tpl) +  class PointChooserWidget(forms.TextInput):      """      Manage the edition of point on a map diff --git a/example_project/settings.py b/example_project/settings.py index dec2827..e78751d 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -5,6 +5,7 @@  # overload all theses settings in your local_settings.py file  import os +_ = lambda s: s  DEBUG = False  TEMPLATE_DEBUG = DEBUG @@ -72,11 +73,33 @@ CHIMERE_OSM_PASSWORD = 'test'  # encoding for shapefile import  CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1' +# enable routing in Chimère +CHIMERE_ENABLE_ROUTING = False + +CHIMERE_ROUTING_TRANSPORT = (('foot', _(u"Foot")), +                             ('bicycle', _(u"Bicycle")), +                             ('motorcar', _(u"Motorcar")), +                            ) + +CHIMERE_ROUTING_SPEEDS = {'foot':((3, _(u"You are walking slowly")), +                                  (6, _(u"You are walking pretty quickly")),), +                          'bicycle':((16, _(u"You are riding pretty slowly")), +                                     (22, _(u"You are riding pretty quickly")),) +                         } + +# available routing engine: 'routino' +CHIMERE_ROUTING_ENGINE = { +    'ENGINE': 'routino', +    'PATH': '/usr/local/src/web/bin/router', +    'DB_PATH': '/var/local/routino/', +} + +NOMINATIM_URL = 'http://nominatim.openstreetmap.org/search' +  # thumbnail  CHIMERE_THUMBS_SCALE_HEIGHT=250  CHIMERE_THUMBS_SCALE_WIDTH=None -  ADMINS = (      # ('Your Name', 'your_email@domain.com'),  ) | 
