PROJET AUTOBLOG


blog'o'm0le

source: blog'o'm0le

⇐ retour index

Nono's Songs : Grease - You're the one that I want

lundi 24 février 2014 à 08:00

Les Nono's Songs n'ont pas de but particulier si ce n'est de publier des musiques que j'apprécie.

Je ne donne aucune appréciation, je poste que des musiques, trouvées au détour du Net, ou d'un son/bruit que j'ai pu entendre quelques part ou avec quelqu'un. Ce n'est en aucun cas un signe de "nouveauté" ou un effet de mode quelconque.

Voici le clip officiel (tiré du film) de Grease qui nous propose You're the one that I want


Autopsie d'une dataviz [5.3] : des switchers sur mapbox.js

dimanche 23 février 2014 à 20:19

Troisième et dernier volet d'une trilogie "dataïste" et cartographique avec la méthode pour obtenir des switchers efficaces sur mapbox.js.

Résumé des épisodes précédents : un cartographe amateur a, après une préparation d'un gros .shp sur QGis, réussi à obtenir une choroplèthe interactive sur TileMill. Reste plus qu'à animer tout ça sur mapbox.js...

Et autant le dire tout de suite : ça n'a pas été une partie de plaisir. Mon Javascript était déjà bien rouillé à la base, mais les développeurs de MapBox ont aussi une manière particulière d'appréhender les calques sur leur app'.

Mais avec beaucoup de pugnacité de mon côté et de patience du leur, je peux maintenant dresser une feuille de route pour ceux qui voudraient programmer un switcher simple et fonctionnel sur mapbox.js.

Un bel exemple obsolète

Tout d'abord, une brève présentation de mapbox.js. Cette librairie est basée essentiellement sur leaflet.js, une librairie de cartographie open source créée par Vladimir Agafonkin.

Outre sa licence, cette librairie a l'atout majeur d'être très bien optimisée pour les OS mobiles, ce qui est plutôt intéressant à une époque où beaucoup de consommateurs d'info s'exportent sur smartphones et tablettes.

Le défi pour enfin terminer ma cartographie électorale du Grand-Est était le suivant : programmer une carte interactive avec un switcher qui permettrait de naviguer entre plusieurs années de scrutin.

En furetant un peu sur la Toile, je suis tombé sur ce boulot titanesque de Bjørn Sandvik avec la Nouvelle-Zélande.

Il avait exactement créé ce que je cherchais à faire, et détaillé par le menu toutes les étapes de son travail sur son blog.

J'ai donc d'abord commencé à essayer d'adapter son exemple avec mes propres cartes.

Voici le résultat, publié premièrement sur Rue89Strasbourg :

D'un point de vue fonctionnel, la carte fait bien le job sur ordinateur, mais j'ai très vite trouvé quelques défauts :

Tout ceci venait malheureusement du code, devenu complètement obsolète après la version 1.0.0 de l'application :

<script>
  var map = mapbox.map('map');
  var layers = document.getElementById('map-ui');

  // on commence par déclarer chacun de nos calques (un par année de scrutin) en appliquant bien 
  // un map.interaction.auto pour synchroniquer les zones interactive avec le chargement de la carte
  map.addLayer(mapbox.layer().composite(false).id('raphadasilva.h5o8i9lo', map.interaction.auto), map.setZoomRange(7, 10));
  map.addLayer(mapbox.layer().composite(false).id('raphadasilva.h5oa0na3', map.interaction.auto), map.setZoomRange(7, 10));
  map.centerzoom({ lat: 48.36, lon: 5.62 }, 7);

  // le switcher est créé à partir du nombre de calques déclarés avant
  for (var i = 0; i < map.getLayers().length; i++) {
      var n = map.getLayerAt(i).name;
      var item = document.createElement('li');
      var layer = document.createElement('a');
          layer.href = '#';
          layer.id = n;
          layer.innerHTML = (2014-5*(i+1));

      if (i === 0) {
          layer.className = 'active';
          map.getLayerAt(i).enable();
      } else {
          map.getLayerAt(i).disable();
      }

      layer.onclick = function(e) {
          e.preventDefault();
          e.stopPropagation();
          // on désactive le calque actif jusqu'à présent
          for (var j = 0; j < map.getLayers().length; j++) {
              map.getLayerAt(j).disable();
              layers.childNodes[j].childNodes[0].className = '';
          }
          // et on active celui qui est sélectionné
          this.className = 'active';
          map.getLayer(this.id).enable();
          map.interaction.refresh();
      };
      item.appendChild(layer);
      layers.appendChild(item);
  }
</script>

Sauf que depuis la 1.0.0, l'intégration de Leaflet était devenue encore plus importante, et cette syntaxe ne permettait pas d'afficher une carte correcte sur OS mobile.

Il fallait donc arriver à une mise à jour sur une version actuelle de l'app'.

Bien distinguer les types de calques

La déclaration d'une carte avec un calque unique sur mapbox.js est ultra simple :

<script type='text/javascript'>

 L.mapbox.map('map', 'raphadasilva.h610d5ea').setView([47.88, 5.5613], 7);

</script>

Ce qui nous donne concrètement avec un MBTile préparé convenable sur TileMill :

En affectant quelques options à une div map, on se retrouve avec une carte qui :

On pourrait facilement imaginer que, pour obtenir un switcher opérationnel, il suffirait de réaffecter de nouvelles options à la div map, comme ceci :

<div id='map' class='map'>
  <div id='layer' class='layers'><a id='carte1' href='#'>2009</a><a id='carte2' href='#'>2004</a></div>
</div>
<script>
// on déclare une première carte
var map = L.mapbox.map('map','raphadasilva.h5o8i9lo', {zoomControl: false}).setView([47.88, 5.5613], 7);

//on fait une fonction pour chaque clic qui vide et remplit
// à nouveau la div 'map'
document.getElementById('carte2').onClick = function() {

  $('#map').empty;
  map = L.mapbox.map('map','raphadasilva.raphadasilva.h5oa0na3', {zoomControl: false}).setView([47.88, 5.5613], 7);
}

document.getElementById('carte1').onClick = function() {

  $('#map').empty;
  map = L.mapbox.map('map','raphadasilva.h5o8i9lo', {zoomControl: false}).setView([47.88, 5.5613], 7);
}

</script>

 Sauf que ça ne marche pas. Mince...

En réalité, la meilleure méthode est de considérer le remplissage de la div 'map' comme un simple fond de carte, sur lequel on va appliquer ensuite des transformations.

Pour cela, il faut bien distinguer :

Ainsi, rien n'empêche d'utiliser un fond de carte bien travaillé sur TileMille ou Mapbox (voir ces adaptations de Snazzy Maps) pour ensuite lui coller des zones interactives. Exemple ici :

Et voici le code associé :

<script>
//on commence par déclarer la carte avec le fond statique
var map = L.mapbox.map('map', 'raphadasilva.h9f3ogf1').setView([47.88, 12.6562], 4);
map.options.maxZoom = 6;
map.options.minZoom = 4;

//on charge les tuiles, sans interaction
var layer = L.mapbox.tileLayer('raphadasilva.hbmoelap');
 map.addLayer(layer);

//on charge le calque avec l'interaction
var gridLayer = L.mapbox.gridLayer('raphadasilva.hbmoelap');
map.addLayer(gridLayer);

//on ajoute la fenêtre interactive qui s'affichera automatiquement
map.addControl(L.mapbox.gridControl(gridLayer));

</script>

Attention tout de même : si le MBTiles qui finira en TileLayer a été paramétré pour s'afficher dans une fourchette de zoom, il faut installer un maxZoom et un minZoom avec cette même fourchette sur la div 'map' pour ne pas qu'il disparaisse.

Un premier switcher pas à pas

Voici un premier switcher fonctionnel :

Et voici le code qui lui est associé :

<ul id='map-ui'>
        <li><a href="#" id="carte2009">2009</a></li>
	<li><a href="#" id="carte2004">2004</a></li>
</ul>
<div id='map'></div>

<script type='text/javascript'>
document.getElementById('carte2009').className = 'active';
//on déclare le fond de carte, avec niveaux de zoom déjà intégrés
var map = L.mapbox.map('map','raphadasilva.had5l5i7', {zoomControl: false}).setView([47.88, 5.5613], 7);

//on déclare chaque tileLayer et gridLayer
layer2009 = L.mapbox.tileLayer('raphadasilva.h5o8i9lo');
grid2009=L.mapbox.gridLayer('raphadasilva.h5o8i9lo');
layer2004 = L.mapbox.tileLayer('raphadasilva.h5oa0na3');
grid2004=L.mapbox.gridLayer('raphadasilva.h5oa0na3');

//on ajoute d'emblée les calques de 2009
map.addLayer(layer2009);
map.addLayer(grid2009);

//on ajoute un gridControl
//le follow: true implique que la fenêtre interactive va suivre le curseur
var gridControl = L.mapbox.gridControl(grid2009, {follow: true}).addTo(map);

//au clic sur le lien "2004", on crée une fonction qui change le CSS
// met à jour le gridControl, efface les calques associés à 2009
//et affiche les calques de 2004
document.getElementById('carte2004').onclick = function(e) {
        e.preventDefault();
        e.stopPropagation();
		document.getElementById('carte2004').className = 'active';
		document.getElementById('carte2009').className = '';
		gridControl = L.mapbox.gridControl(grid2004, {follow: true}).addTo(map);
            map.removeLayer(layer2009);
            map.removeLayer(grid2009);
			map.addLayer(layer2004);
			map.addLayer(grid2004);

    };

//on fait une fonction semblable avec le lien "2009"
document.getElementById('carte2009').onclick = function(e) {
        e.preventDefault();
        e.stopPropagation();
		document.getElementById('carte2009').className = 'active';
		document.getElementById('carte2004').className = '';
			gridControl = L.mapbox.gridControl(grid2009, {follow: true}).addTo(map);
            map.removeLayer(layer2004);
            map.removeLayer(grid2004);
			map.addLayer(layer2009);
			map.addLayer(grid2009);
    };

</script>

 Passage à la boucle for

Il sera peut-être plus évident d'automatiser la méthode avec une boucle "for" pour arriver à ce résultat :

Soulignons l'importance du gridControl dans ce cas précis.

Si on l'oublie, les fenêtres interactives se superposent à chaque changement de calque, et on finit par ne plus s'y retrouver. Heureusement, une fonction gridControl.hide() permet d'y parvenir facilement.

Passons enfin au code :

<div id=map class=map>
<div id=layers class=layers></div>
</div>

<script>
//on paramètre le fond de carte
var map = L.mapbox.map('map', 'raphadasilva.had5l5i7', {
    zoomControl: false
}).setView([48.18, 5.5613], 7);

// on met dans un tableau les identifiants des tileLayers/gridLayers
var maps = [{
    id: 'raphadasilva.h5o8i9lo'
}, {
    id: 'raphadasilva.h5oa0na3'
}];
var layers = document.getElementById('layers');

//ne surtout pas oublier le gridControl
var gridLayer;
var gridControl;
var layer;

//on crée à chaque entrée du tableau un lien relié aux calques de chaque id
for (var i = 0; i < maps.length; i++) {
    var link = document.createElement('a');
    link.href = '#';
    link.innerHTML = 2014 - 5 * (i + 1);
    link.setAttribute('data-layer', i);
    link.onclick = function () {
        if (/active/.test(this.className)) {
            this.className = this.className.replace(/active/, '').replace(/\s\s*$/, '');
        } else {
            var siblings = layers.getElementsByTagName('a');
            for (var i = 0; i < siblings.length; i++) {
                siblings[i].className = siblings[i].className.replace(/active/, '').replace(/\s\s*$/, '');
            };
            this.className += ' active';
            var m = maps[this.getAttribute('data-layer')];

            // on vire chaque layer/gridLayer/gridControl
            if (layer) map.removeLayer(layer);
            if (gridLayer) map.removeLayer(gridLayer);
            if (gridControl) map.removeControl(gridControl);

           //on met à jour les variables...
            gridLayer = L.mapbox.gridLayer(m.id);
            layer = L.mapbox.tileLayer(m.id);
            gridControl = L.mapbox.gridControl(gridLayer);

           //et on les affecte à la div 'map'
            map.addLayer(layer);
            map.addLayer(gridLayer);
            map.addControl(gridControl);
        }
        return false;
    };
    if (i === 0) link.onclick();
    layers.appendChild(link);
}

};

 Un template gitHub

Pour ceux que ça intéresse, j'ai mis en ligne un template du dernier switcher sur gitHub.

Dès que je trouverai le temps, j'essayerais de montrer des switchers plus élaborés, notamment avec des fonds de carte changeants.

Des Snazzy Maps sur MapBox

lundi 17 février 2014 à 10:38

Quelques fonds de carte MapBox librement inspirés de merveilles trouvées sur Snazzy Maps. Idéal pour en mettre plein la vue !

Toutes ces ressources étant en Creative Commons, il aurait été dommage de s'en priver pour les réexploiter sur MapBox :-) !

Advocado World

Id : raphadasilva.h9844m0j

Blue Essence

Id : raphadasilva.h9fa4pme

Deep Green

Id : raphadasilva.h9f97dg6

Homage to Toner

Id : raphadasilva.h9f8e8j0

Neutral Blue

Id : raphadasilva.h982kjhn

Nightvision

Id : raphadasilva.h986ce52

Red Alert

Id : raphadasilva.h9f26f54

Roadtrip at Night

Id : raphadasilva.h98621bf

Subtle Greyscale

Id : raphadasilva.h986nman

Unimposed Topography

Id : raphadasilva.h9f3ogf1

Vintage Blue

Id : raphadasilva.h9faifpd

Nono's Songs : Bernard Lavilliers - On the road again

lundi 17 février 2014 à 08:00

Les Nono's Songs n'ont pas de but particulier si ce n'est de publier des musiques que j'apprécie.

Je ne donne aucune appréciation, je poste que des musiques, trouvées au détour du Net, ou d'un son/bruit que j'ai pu entendre quelques part ou avec quelqu'un. Ce n'est en aucun cas un signe de "nouveauté" ou un effet de mode quelconque.

Voici le clip officiel de Bernard Lavilliers qui nous propose On the Road Again

Autopsie d'une dataviz [5.2] : des MBTiles pour éviter les tuiles

mardi 11 février 2014 à 06:30

Comment faire pour afficher proprement et sans lenteur une carte choroplèthe contenant des milliers de zones ? Réponse dans cette seconde partie d'une trilogie cartographique !

Dans l'épisode précédent :

Un cartographe amateur s'est lancé dans un projet un peu fou. Il souhaite créer des cartes interactives contenant des milliers d'informations, et est déjà parvenu, au prix d'efforts harassants, à obtenir un fichier .shp réutilisable. Reste plus qu'à le transformer en carte...

Première difficulté : notre .shp pèse environ 60 Mo... Erf, ça s'annonce dur pour l'afficher convenablement dans un service de cartographie numérique !

Même en passant par Mapshaper pour simplifier tout ça, ce ragoût de données fera ramer à coup sûr Google Fusion Tables sur n'importe quelle machine...

Les MBTiles à la rescousse

Les tarés de MapBox ont heureusement une solution à proposer : les MapBox Tiles (aujour'hui transformées en Vector Tiles).

Le principe, pour ce que j'en ai compris, est grosso modo le suivant :

Plus de ramage, plus de plantage, et en prime de l'interactivité et un poids réduit (dans notre cas environ 11 Mo). Que demande le peuple :-) ?

Première choroplèthe avec TileMill

En ouvrant un nouveau projet sur TileMill, on obtient un écran similaire au suivant :

tuto_choro_tm

Cliquez sur l'image pour l'agrandir

Par défaut, TileMill fournit un fichier de l'ensemble des nations mondiales, avec leurs frontières respectives. En bas à gauche, on voit le seul calque pour l'instant présent, #countries.

En haut à droite, on distingue le code CartoCSS qui permet de colorer le fond de carte, les frontières et les pays :

Map {
  background-color: #b8dee6;
}

#countries {
  ::outline {
    line-color: #85c5d3;
    line-width: 2;
    line-join: round;
  }
  polygon-fill: #fff;
}

A la base, on définit la couleur du fond de carte qui, en-dessous des pays affichés en blanc, correspondra effectivement aux mers et océans. Du coup, en changeant simplement trois couleurs, on obtient le fond de carte suivant :

tuto_choro_tm_2

Cliquez sur l'image pour l'agrandir

Pour l'instant, rien de vraiment bien méchant, on a simplement changé le code comme suit :

Map {
  background-color: #fff;
}

#countries {
  ::outline {
    line-color: #f0f0f0;
    line-width: 2;
    line-join: round;
  }
  polygon-fill: #000;
}

On va maintenant charger le .shp obtenu précédemment. Pour cela, il faut le copier dans Mes Documents, ou en tout cas ailleurs que sur un disque dur externe.

Ensuite, on clique sur l'icône de calque tout en bas à gauche, puis sur "Add layer".

tuto_choro_tm_3

De là, on va cliquer sur "Browse" à côté du champ "Datasource" pour sélectionner le .shp avec toutes les données, puis cliquer sur "Save". Si le shapefile est correct, on se retrouve avec un nouveau calque au-dessus du précédent.

tuto_choro_tm_4

Si l'on clique sur l'icône en forme de tableur juste à côté du calque #europeennes2009geabs, voici ce que l'on peut observer :

tuto_choro_tm_5

Cliquez sur l'image pour l'agrandir

Pour rapidement résumer :

Les données rattachées au calque ont l'air propres, mais on peut tout de même vérifier le bon affichage des zones contenus dans ce .shp en ajoutant ces quelques lignes au CartoCSS :

#europeennes2009geabs{
  polygon-fill: #fff;
}

Après avoir cliqué sur "Save" en haut à droite et zoomé un tantinet, voici ce que l'on obtient dans notre cas :

tuto_choro_tm_6

Cliquez sur l'image pour l'agrandir

Sacré morceau, n'est-ce pas ? Reste plus qu'à programmer la fameuse choroplèthe :-) !

Le diable est dans les détails

Ce mantra, vous le lirez très souvent sur les tutoriels de ce blog, et pour cause : le diable est toujours dans les détails !

Dans le cas des choroplèthes, on peut par exemple hésiter entre un un éventail de couleurs séquentiel ou divergent.

Et les deux ne donneront pas du tout la même lisibilité, comme le montre l'exemple suivant (cliquez sur les calques en icônes pour changer de type de cartes) :

Etonnant, non ? Et comme il faut rendre à César ce qui est à César, un grand merci  à Thierry Labro pour m'avoir conseillé le premier d'utiliser ce type de choroplèthe alors que j'étais parti sur une séquentielle.

La traduction en CartoCSS, obtenue à partir de la colonne ABS_INS et d'un éventail pioché sur colorbrewer, se trouve ci-après :

#europeennes2009geabs::fill[zoom>=0] {
  [ABS_INS > 75]{polygon-fill:#d7191c;}
  [ABS_INS > 65.0][ABS_INS <= 75.0]{ polygon-fill:#fc8d59; }
  [ABS_INS > 55.0][ABS_INS <= 65.0]{ polygon-fill:#fee08b; }
  [ABS_INS > 45.0][ABS_INS <= 55.0]{ polygon-fill:#ffffbf; }
  [ABS_INS > 35.0][ABS_INS <= 45.0]{ polygon-fill:#d9ef8b; }
  [ABS_INS > 25.0][ABS_INS <= 35.0]{ polygon-fill:#91cf60; }
  [ABS_INS <= 25]{polygon-fill:#1a9850;}
}

Les plus observateurs auront également remarqué plus-haut les contours des départements. Ils sont obtenus après avoir créé sur QGis un fichier des départements en fusionnant toutes les villes de chacun d'entre eux.

Il ne reste ensuite plus qu'à charger ce calque, et à n'afficher que ses contours comme ceci :

#deptsge {
  ::outline {
    line-color: #000;
    line-width: 1;
    line-join: round;
  }
}

La choroplèthe a déjà pas mal de gueule, mais ce serait encore mieux si on ajoutait une légende et de l'interaction. Ça tombe bien, on peut faire tout ça depuis TileMill :-) !

Légende et fenêtre interactive

Passons maintenant à l'étape suivante avec la mise en place d'une légende sur notre belle carte. On va directement s'inspirer d'un des exemples fournis par l'équipe de MapBox pour la réaliser.

Ce code, on va tout simplement le placer dans une zone prévue à cet effet. Pour l'atteindre, il suffit de cliquer sur l'icône en forme de main, en bas à gauche de l'interface :

tuto_choro_tm_7

Cliquez sur l'image pour l'agrandir

Et voici le code qui anime tout ça :

<div class='my-legend'>
<div class='legend-title'>L'abstention aux européennes de 2009<br/> (circonscription Grand-Est)</div>
<div class='legend-scale'>
  <ul class='legend-labels'>
    <li><span style='background:#d7191c;'></span>plus de 75%</li>
    <li><span style='background:#fc8d59;'></span>entre 65% et 75%</li>
    <li><span style='background:#fee08b;'></span>entre 55% et 65%</li>
    <li><span style='background:#ffffbf;'></span>entre 45% et 55%</li>
    <li><span style='background:#d9ef8b;'></span>entre 35% et 45%</li>
    <li><span style='background:#91cf60;'></span>entre 25% et 35%</li>
    <li><span style='background:#1a9850;'></span>moins de 25%</li>
  </ul>
</div>
<div class='legend-source'>Source: <a href="http://www.data.gouv.fr/" target="_blank">Data.gouv</a></div>
</div>

<style type='text/css'>
  .my-legend .legend-title {
    text-align: left;
    margin-bottom: 5px;
    font-weight: bold;
    font-size: 90%;
    }
  .my-legend .legend-scale ul {
    margin: 0;
    margin-bottom: 5px;
    padding: 0;
    float: left;
    list-style: none;
    }
  .my-legend .legend-scale ul li {
    font-size: 80%;
    list-style: none;
    margin-left: 0;
    line-height: 18px;
    margin-bottom: 2px;
    }
  .my-legend ul.legend-labels li span {
    display: block;
    float: left;
    height: 16px;
    width: 30px;
    margin-right: 5px;
    margin-left: 0;
    border: 1px solid #999;
    }
  .my-legend .legend-source {
    font-size: 70%;
    color: #999;
    clear: both;
    }
  .my-legend a {
    color: #777;
    }
</style>

La magie opère immédiatement après avoir sauvegardé :

tuto_choro_tm_8

Cliquez sur l'image pour l'agrandir

Pourquoi s'arrêter en si bon chemin ? Installer des fenêtres interactives au survol de chaque commune n'a rien de bien méchant.

Il suffit encore une fois de se rendre dans la zone spécifique en cliquant de nouveau sur l'icône en forme de main, mais en se rendant cette fois-ci dans le menu "Teaser".

tuto_choro_tm_9

Cliquez sur l'image pour l'agrandir

Il suffit, dans le menu déroulant en bas à gauche, de choisir le calque qui contient les données à afficher. TileMill affiche chaque colonne entre trois {}. On a plus qu'à coder intuitivement notre div comme ceci :

<div>
<strong>{{{COMMUNE}}}</strong><br>
Inscrits : {{{INSCRITS}}} <br>
Abstentions : {{{ABSTENTION}}}<br>
Abstention : {{{ABS_INS}}} % des inscrits
</div>

Et après avoir, sauvegardé, on a enfin un résultat final plus que chouette :

tuto_choro_tm_10

Cliquez sur l'image pour l'agrandir

Bref, tout est prêt pour l'export :-) !

Quelques astuces pour un MBTiles léger

Il est temps de préparer un fichier MBTiles que l'on hébergera ensuite sur MapBox ou un serveur perso. Pour cela, on clique sur "Export" tout en haut à droite, et on choisit "MBTiles".

Voici deux trucs pour ne pas avoir un fichier trop lourd à l'arrivée :

Dans notre cas, un export optimal serait le suivant :

tuto_choro_tm_11

Cliquez sur l'image pour l'agrandir

Une fois le fichier chargé dans la file d'attente, plus qu'à cliquer sur "Save" pour le voir immédiatement débarquer par défaut dans "Mes Documents".

A suivre

Au menu de la troisième et dernière partie de ce tutoriel : utiliser la librairie mapbox.js pour installer un switcher qui conserve l'interactivité des fameux MBTiles.