Mezzanine et OpenStreetMap sont dans un bateau

Je l'ai déjà écrit plus tôt : Mezzanine est un chouette projet. Toujours dans l'optique de jouer avec ce bel outil, je me suis assigné la tâche d'afficher une joli carte quelque part. Comme ça, juste parce que. Voyons comment faire.

Parlons cartographie

À peu près tout ce que je sais sur la cartographie (c'est à dire le strict minimum, je l'avoue), je l'ai appris durant les rencontres Django 2012, lors de l'excellente introduction à ce noble art par Mathieu Leplatre. De quoi avons nous besoin pour afficher une carte interactive, sans toutefois passer par l'hégémonique Gogole ? Simple :

  • des données cartographiques ;
  • des tiles, rendu de ces données sous forme d'images ;
  • un serveur de tiles ;
  • une librairie de rendu côté client ;

Si l'on souhaitait un rendu particulier, ou l'affichage de données spécifiques (issues d'un projet Open-data, peut-être ?), on pourrait créer nos propres tuiles grâce à TileMill, outil fort bien conçu qui permet de styler et d'exporter des cartes grâce à une syntaxe proche du CSS. Mais nous ne pousserons pas la perversion jusque là, et nous contenterons de tuiles existantes.

Les données cartographiques seront donc gracieusement fournies par OpenStreeMap, base de données cartographiques disponibles sous une licence libre. Pour l'exemple, nous utiliserons directement le serveur de tuile du projet, mais un usage plus massif nécessiterait de passer par un hébergeur spécialisé (comme Cloudmade), ou de servir nous mêmes nos propres tuiles. Enfin, le rendu côté client sera géré par le projet Leaflet.

Affichons une carte dans Django

Cette accumulation de technologies ferait frémir de plaisir tout amateur de buzz bingo. Pourtant, la mise en place de tout ce fatra est d'une simplicité à pleurer (de rire), vous allez voir.

$ pip install django-leaflet

Puis dans vos settings :

INSTALLED_APPS = (
    
    "leaflet",
    
)

LEAFLET_CONFIG = {
    'TILES_URL': 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    # Centrons la carte sur Montpellier :
    'SPATIAL_EXTENT': (3.264, 43.91, 4.456, 43.305),
    'DEFAULT_CENTER': (43.598501, 3.896847),
    'DEFAULT_ZOOM': 12,
}

Pouf, c'est installé. Tout ce qu'il vous reste à faire, c'est de créer une nouvelle vue, avec un template qui ressemble à ça :

{% extends 'base.html' %}
{% load leaflet_tags %}

{% block extra_head %} {{ block.super }}
    {% leaflet_js %}
    {% leaflet_css %}
{% endblock %}

{% block main %}
    {% leaflet_map "citymap" %}
{% endblock %}

{% block extra_js %} {{ block.super }}

    function citymapInit(map, bounds) {
        L.marker([43.598503, 3.896847]).addTo(map).bindPopup("L'hôtel de ville");
    }

{% endblock %}

Et vous voilà avec une sympathique carte centrée sur la belle ville de Montpellier, l'hôtel de ville indiqué par un marqueur. Ça va, je n'ai perdu personne ?

Et Mezzanine, dans tout ça ?

Que se passe-t-il si je souhaite afficher ma carte, non sur une page vide, mais en l'intégrant dans une page gérée par Mezzanine ? C'est à peine plus compliqué.

D'abord, il faut effectivement créer la page côté backend.

Création d'une page avec Mezzanine

Notez bien le champ de la valeur « URL » dans les méta-données de la page. C'est cette valeur que nous allons utiliser pour faire le lien entre notre page et notre vue. Dans le fichier urls.py :


url("^la-ville/cartographie/$", "city_map.views.map", name="city_map"),
("^", include("mezzanine.urls")),

Oui, je sais, faire dépendre d'une valeur modifiable en admin le bon fonctionnement de son appli, c'est trés moyen. C'est comme ça.

La vue en question ne pourrait pas être plus simple :

from mezzanine.utils.views import render


def map(request):
    return render(request, 'map.html')

Le nouveau template est à peine plus compliqué :

{% extends 'pages/richtextpage.html' %}
{% load leaflet_tags %}
{% load mezzanine_tags %}

{% block extra_head %} {{ block.super }}
    {% leaflet_js %}
    {% leaflet_css %}
{% endblock %}

{% block main %}
    {% leaflet_map "citymap" %}

    {% editable page.richtextpage.content %}
        {{ page.richtextpage.content|richtext_filter|safe }}
    {% endeditable %}
{% endblock %}

{% block extra_js %} {{ block.super }}

    function citymapInit(map, bounds) {
        L.marker([43.598503, 3.896847]).addTo(map).bindPopup("L'hôtel de ville");
    }

{% endblock %}

Le résultat n'est-il pas d'une troublante beauté ?

Carte sous Mezzanine