Autopsie d'une dataviz [6] : une mini app' avec MapBox

Retour sur la réalisation d'une web app' géographique assez basique pour Rue89Strasbourg. Au programme : geoJson, Highcharts, QGis et mapbox.js !

En soi, cette web app' n'est techniquement pas très compliquée, et est très largement inspirée de cet exemple disponible sur le site de Mapbox.

Concrètement, elle est codée pour :

  • afficher des zones géographiques, formatées en geoJson et préalablement coloriées (bleu pour la droite, rouge pour la gauche, gris pour les sans étiquettes)
  • mettre à jour, à chaque clic sur une zone, une div 'info' avec un titre, un texte et un chargement d'iframe paramétré depuis le geoJson
  • chaque iframe contient un graphe Highcharts avec les résultats des dernières municipales

Côté code, voici celui des résultats de Strasbourg :

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>

<style>
.highcharts-tooltip span {
    height:auto;
    width:70px;
    white-space:normal !important;
}

</style>
<div id="container" style="width: 180px; height: 280px; margin-left:-18;"></div>
<script>
$(document).ready(function () {
        $('#container').highcharts({
        chart: {
            type: 'column'
        },
        title: {
            text: 'Mun. 2008'
        },
        subtitle: {
            text: 'Source: data.gouv.fr'
        },
        xAxis: {
            categories: [
                'PS',
                'UMP'
            ]
        },
        yAxis: {
            min: 0,
            title: {
                text: 'Voix (%)'
            }
        },
        tooltip: {
            headerFormat: '<span style="font-size:16px; width:75px;">{point.key}</span><table>',
            pointFormat: '<tr><td style="padding:0"><b>{point.y:.1f} %</b></td></tr>',
            shared: true,
            useHTML: true
        },
        plotOptions: {
            column: {
                pointPadding: 0.2,
                borderWidth: 0
            }
        },
        series: [{
            showInLegend: false,
            name:'Voix',
            data: [{y:58.3,color:'#fc83b4'}, {y:41.7,color:'#4176e8'}],
            dataLabels: {
                enabled: true,
                rotation: -90,
                color: '#FFFFFF',
                align: 'right',
                x: 4,
                y: 10,
                style: {
                    fontSize: '13px',
                    fontFamily: 'Verdana, sans-serif',
                    textShadow: '0 0 3px black'
                }}

        }]
        });
    });
</script>

Ce qui, une fois bien digéré, nous donne :

Attention aux MultiPolygon !

Côté déclaration des geoJson dans un script MapBox, il n'y a rien de bien sorcier.

Voici la déclaration d'une zone, avec comme info un nom de communes, quelques habillages graphiques, et un texte type :

var geoJson = [
{ "type": "Feature", "properties": 
{ "COMMUNE": "Eschau", "fill": "#4176e8", "stroke-width": 1.3, 
"fill-opacity": 0.85, "stroke": "#000",  "stroke-opacity": 0.5, 
"texte":"La liste conduite par le maire sortant Jean-Louis Feyd (divers droite) remporte haut la main le premier tour, face à celle dirigée par Alain Caps (divers droite)." }, 
"geometry": { "type": "Polygon", 
"coordinates": [ [ [ 7.693311421900148, 48.452983290737983 ],[blablabla...], 
[ 7.69378591203322, 48.453527664443321 ] ] ]} }
];

Sauf que les choses se gâtent très rapidement si, au lieu d'un "Polygon", nous avons un "MultiPolygon".

MapBox n'arrive tout simplement pas à interpréter ce type de géométrie, et sera incapable de colorier la zone ou de retourner ses autres valeurs.

Dans notre cas, ça arrive quand une ville a un ban communal éclaté et pas uniforme, comme par exemple Schiltigheim (trois Stück quand même !).

Et heureusement, on peut utiliser l'incontournable QGis pour diviser un MultiPolygon en plusieurs Polygon.

Après avoir ouvert le fichier vectoriel contenant les zones géographiques, il suffit d'aller dans > Vecteur > Outils de géométrie > Scinder une entité multigéométrie :

App Mapbox1

Une fenêtre s'ouvre alors proposant de choisir un emplacement et un nom pour le shapefile qui n'aura plus de MultiPolygon :

App Mapbox2

Une fois ceci fait, QGis propose d'ouvrir directement la couche générée, ce qui permet de rapidement vérifier que la manip' a marché.

Dans notre cas on voit bien, après un clic droit sur la couche et une sélection de "Ouvrir la table des attributs, plusieurs polygones qui portent le même nom :

App Mapbox3

Evidemment, rien n'interdit de convertir ce .shp en un autre format vectoriel, comme le geoJson !

Le code sous mapbox.js

Une fois le geoJson ok, on peut se mettre au code, ce qui nous donne :

<script>
var map = L.mapbox.map('map', 'raphadasilva.h986nman', {zoomControl: false})
    .setView([48.57, 7.65], 11);

// on désactive toutes les formes de zoom et le dragging    
map.dragging.disable();
map.touchZoom.disable();
map.doubleClickZoom.disable();
map.scrollWheelZoom.disable();

// on déclare un geojson contenant des coordonnées géographiques, des noms, des infos de style et du texte
// les multiPolygon ont été segmentés en plusieurs Polygon
var geoJson = [blablabla, voir plus haut, tout ça, tout ça];

// on charge le geoJson en tant que calque
map.featureLayer.setGeoJSON(geoJson);

//à chaque clic sur une zone de ce calque, on crée une fonction qui...
map.featureLayer.on('click',function(e) {

// désactive la pop-up automatique
    e.layer.closePopup();

//vide le contenu de la div 'info'
    document.getElementById('info').innerHTML = '';

    var feature = e.layer.feature;

//met dans une variable un titre, un texte et une iframe dérivant directement des propriétés du geoJson
    var info = '<h3>' + feature.properties.COMMUNE + '</h3><p>' + feature.properties.texte +'</p><iframe src="' + feature.properties.COMMUNE + '.html" height="290" width="180" frameborder="0" scrolling="no"></iframe>';

//remplit l'infobox
    document.getElementById('info').innerHTML = info;

});

// si on clique sur une zone de la carte autre, on remet l'infobox comme au début
map.on('click',function(e){
    document.getElementById('info').innerHTML = '<h4>Cliquez sur une ville de la CUS</h4>';
});

</script>

Ce qui donne très concrètement à l'arrivée :

Pour aller plus loin

Le code entier de l'app' a été publié sur gitHub.

Par Raphaël da Silva dans la catégorie
Tags : #mapbox, #mapboxjs, #app,