Playing - Simulation de crue à Paris

Google Elevation API, Leaflet et D3JS
d3js3.x leaflet0.7.3 jquery1.9.1
Sources :

Introduction

La cartographie, accessible en cliquant sur l’image ci-dessus, est composée de 13022 points géolocalisés dans la ville de Paris. En plus de la latitude et de la longitude, chaque point possède la valeur de son élévation. A partir de ces données nous construisons une surcouche qui agrège les points sous la forme d’hexagones. A chaque hexagone est attribué une couleur en fonction de l’altitude moyenne des points qui le composent. Enfin, si l’altitude calculée d’un hexagone est inférieure à la hauteur de crue sélectionnée alors cet hexagone sera bleu. Les hexagones sont recalculés à chaque fois que le niveau de zoom sur la carte change. Nous verrons dans les parties suivantes comment construire ces données et les mettre en œuvre au travers d’une librairie Leaflet quelque peu adaptée pour notre besoin.

Construction de la carte et délimitation de la ville de Paris

Nous commençons par représenter le fichier geoJSON departement.json sur une carte Leaflet. Par simplicité celui-ci est intégré dans le fichier departement.js.

var map = L.map('map1').setView([48.85841, 2.3488], 12);

var stamenToner = L.tileLayer('http://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', {
	attribution: 'Map tiles by Stamen Design, CC BY 3.0 - Map data © OpenStreetMap',
	subdomains: 'abcd',
	minZoom: 0,
	maxZoom: 20,
	ext: 'png'
});

map.addLayer(stamenToner);

/* L'attribut department provient du fichier JS et contient le geoJSON */
var gjLayer = L.geoJson(departement, {style: style}).addTo(map);

function style(feature) {
	return {
		weight: 4,
		opacity: 1,
		color: '#1f8dd6',
		dashArray: '8',
		fillOpacity: 0
	};
}

Génération des coordonnées

Nos données représentent l'ensemble des points contenus dans la ville de Paris espacés d'un pas constant. Pour cela nous parcourons le rectangle contenant la ville de Paris (il nous sert de bornes min & max) et nous utilisons la librairie leaflet-pip qui permet de savoir si un point est dans un polygone pour Leaflet. Le pas choisi ici est de 0.01 tant pour la latitude que pour la longitude. Pour générer nos données il était de 0.001, beaucoup plus précis donc.

var lat = gjLayer.getBounds().getSouth();
var lng = gjLayer.getBounds().getWest();
var limLat = gjLayer.getBounds().getNorth();
var limLng = gjLayer.getBounds().getEast();
while (lat  <= limLat) {
	// lat et lng sont inversées
	if (leafletPip.pointInLayer([lng, lat], gjLayer).length != 0) {
		L.marker([lat, lng]).addTo(map);
	}
	lng += 0.01;
	if (lng > limLng) {
		lat += 0.01;
		lng = gjLayer.getBounds().getWest();
	}
}

Récupération d'altitude via Google Elevation API

En utilisant JQuery pour effectuer une requête il est très facile de récupération l'élévation avec l'API de Google. En revanche avec une clé gratuite, on est limité à la fois en nombre de requêtes par jour et à la fois dans un laps de temps donné. A partir de ce code nous avons pu construire le fichier locations.js qui contient les 13022 points utilisés dans notre visualisation.

$.ajax({
	url: 'https://maps.googleapis.com/maps/api/elevation/json?locations=' + lat + "," + lng + '&key=YOUR_API_KEY',
	type: 'GET',
	dataType: "json",
	header: {'Access-Control-Allow-Origin': 'https://maps.googleapis.com/'},
	crossDomain: true,
	idx: i,
	success: function (data) {
		if (data.status == 'OVER_QUERY_LIMIT') {
			// Trop de requêtes effectuées
		} else {
			// data.results[0].elevation contient l'élévation
		}
	},
	error:function (response) {          
		// response contient une erreur
	}
});

Création des hexagones

La création des hexagones nécessites deux librairies JS toutes deux trouvées sur ce site : Hexbins and D3 Map. La première permet la construction mathématiques des hexagones : hexbin.js. La seconde représente un Layer que l'on peut ajouter à une carte Leaflet. Ce Layer utilise bien sur la première librairie : leaflet-hexbin.js.

Modification de librairie

La librairie leaflet-hexbin.js a été modifié pour rendre facultative la gestion de la souris et pour autoriser la mise à jour du style lorsque l'utilisateur change la hauteur de crue.

Pour construire les hexagones nous commençons par convertir le fichier location.js au format geoJSON (c'est le format qu'utilise la librairie leaflet-hexbin.js).

function reformat(locations) {
	var data = [];
	locations.map(function (d){
		data.push({
			properties: {
				alt: +d.alt
			}, 
			type: "Feature", 
			geometry: {
				coordinates:[+d.lng, +d.lat], 
				type:"Point"
			}
		});
	});
	return data;
}
var geoData = { type: "FeatureCollection", features: reformat(/*locations.js*/locations) };

Nous construisons un scale pour associer la plage d'altitude avec notre plage de couleurs.

var scale = d3.scale.linear()
	.domain([d3.min(locations, function(d) { return d.alt; }),
		 d3.max(locations, function(d) { return d.alt; })])
	.range(["#5eb95e", "#dd514c"]);

Enfin nous construisons notre Layer en commençant par définir la fonction hexbinStyle qui déterminera la couleur de chaque hexgone. Si l'altitude moyenne de l'hexagone est inférieure à la hauteur de crue, alors il est bleu sinon on utilise la fonction scale. L'option mouse dans la construction du Layer permet de gérer une action lorsque l'on clique sur l'hexagone.

function hexbinStyle(hexagons) {
	hexagons
		.attr("stroke", "black")
		.attr("fill", function (d) {
			if (d[0][2].alt <= currentAltitude) {
				return "#1f8dd6";
			}
			var avg = d3.mean(d, function(d) {
				return +d[2].alt;
			})
			return scale(avg);
		});
}

var hexLayer = L.hexbinLayer(geoData, {
		style: hexbinStyle,
		//mouse: makePie
	}).addTo(map);

Pour la gestion des commandes vous pouvez jeter un oeil au code source, il n'y a rien de compliqué. Lorsque l'utilisateur fait varier la hauteur de crue nous appelons la fonction hexLayer.updateStyle() pour mettre à jour les couleurs de chaque hexagone.

COMMENTAIRES

DIOUCK ABDOU


Bonjour
C'est tres enrichissant votre tuto. Selement j'essaye d'adapter votre requete ajax pour convertir un api json en geojson.
http://data.nantes.fr/api/p....

Avez vous une idée pour une itégrtion dans leaflet?

Merci de votre compréhension

Voici la syntaxe

$.ajax({
async: false,
url: 'http://data.nantes.fr/api/p...,
type: 'POST',
dataType:"json",
header: {'Access-Control-Allow-Origin': 'http://data.nantes.fr/'},
crossDomain: true,
success: function(json) {
local=json;
}
}).complete(function () {
var urlvar = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
urlvar[key] = value;
});
if (urlvar.hasOwnProperty('x') && urlvar.hasOwnProperty('y') && urlvar.hasOwnProperty('z')) {
map.setView([parseFloat(urlvar['y']), parseFloat(urlvar['x'])], parseInt(urlvar['z']));
}

});

var test=new L.geoJson(local, {
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {
icon: L.AwesomeMarkers.icon({
icon: 'home',
color: 'cadetblue',
//spin: true
prefix:'fa'
}),
title: feature.properties.nom_comple
});
} ,
onEachFeature:onEachFeature
});

ericfrigot


Bonjour,
Merci pour le compliment. Concernant vos données, que voulez-vous en faire ? Les mettre sur un carte pour faire des marqueurs ? Si c'est le cas vous devriez d'abord récupérer votre fichier JSON et le lire comme je le fais dans les tutoriels de la section "Maps". Si vous m'en dites un peu plus sur votre objectif, je pourrais surement vous aider. Sinon merci de m'avoir fait décrouvrir AwesomeMarkers, je ne connaissais pas.

Eric

DIOUCK ABDOU


Bonjour
Oui en effet au lieu de telecharger la donnée, l'idée c'est de requetter directement l'API de l'open data de Nantes et les afficher sur une carte leaflet. j'ai dea fait des tentatives pour le parser en php en vain. Mais ton tuto m'a tres l'air plus adapté pour le faire

ericfrigot


J'ai fait ça : http://datavis.fr/playing/l...
Le truc c'est que pour éviter les problèmes de cross domain j'ai directement inclus les données dans mon code javascript, mais je ne sais pas si ça vous conviendra.
Eric

DIOUCK ABDOU


Oui super intéressant ca rend la même chose si on télécharge les données en shp ou csv. Mais les données sont mise à jour régulièrement. Et c'est donc pour éviter de les telecharger que je cherche à requêter directement via l'API. Je ne sais pas si c'est assez claire .

ericfrigot


Bonjour, si vous voulez que les données soient mises à jour j'imagine que vous travaillez dans un contexte professionnel. Dans ce cas là la meilleure solution c'est d'utiliser un serveur Proxy et avec Apache c'est très facile à mettre en œuvre (par exemple : http://stackoverflow.com/qu....

DIOUCK ABDOU


En faite les données sont mise à jour non pas par moi mais par le site.

Sinon c'est superbe votre doc, ce n'est pas dans le cadre pro c'est juste pour se former sur l'appel des api.

Sinon j'ai repris votre exemple en rajouter un lien php qui appelle directement la donnee:

Même si ca ne resoud pas totalement le problème ça me depane un peu. Reste plus qu'a rajouter les popup