Chez Liip, nous venons de mettre au point une application destinĂ©e aux Ă©tudiant·e·s en architecture. Celle-ci utilise les shapefiles comme donnĂ©es de base pour fournir des informations sur les terrains constructibles dans la ville de Zurich. Ils apparaissent sur une carte et peuvent ĂȘtre consultĂ©s par lâutilisateur·trice. Un projet similaire a Ă©tĂ© menĂ© par une Ă©quipe dâĂ©tudiant·e·s, dont je faisais partie, pour la filiĂšre FHNW HT: une application web ludique qui permet aux enfants de lâĂ©cole primaire dâapprendre Ă connaĂźtre leur commune.
Non seulement les shapefiles contribuent Ă amĂ©liorer lâexpĂ©rience utilisateur·trice, mais ils contiennent Ă©galement des donnĂ©es qui, lorsquâelles sont mises en contexte, peuvent fournir des informations intĂ©ressantes. Regardons ce que je peux trouver en fouillant dans quelques shapefiles et en crĂ©ant une petite application pour les explorer.
Dans ce billet de blog, jâadopte une approche pratique de lâutilisation des shapefiles et je vous montre comment vous pouvez les utiliser.
La Convention alpine
Pour utiliser des shapefiles, il faut dâabord comprendre de quoi il sâagit exactement et pouvoir sâen procurer.
Les shapefiles sont des fichiers binaires qui contiennent des donnĂ©es gĂ©ographiques. Ils se prĂ©sentent gĂ©nĂ©ralement par groupes dâau moins 3 fichiers diffĂ©rents qui portent tous le mĂȘme nom. Ceux-ci se diffĂ©rencient seulement par leur extension: .shp, .shx et .dbf. Cependant, un ensemble complet de donnĂ©es peut contenir beaucoup plus de fichiers. Ce format de donnĂ©es a Ă©tĂ© mis au point par Esri et introduit au dĂ©but des annĂ©es 1990. Les shapefiles contiennent des « caractĂ©ristiques », câest-Ă -dire des formes gĂ©omĂ©triques avec leurs mĂ©tadonnĂ©es. Une caractĂ©ristique peut ĂȘtre un point (coordonnĂ©es X/Y uniques) et un texte correspondant Ă ce point. Ou un polygone, constituĂ© de plusieurs points X/Y, dâune ligne simple, dâune ligne multiple, etc.
Pour obtenir des shapefiles, je consulte le site OpenData.swiss. OpenData propose 102 ensembles de donnĂ©es de shapefiles diffĂ©rents ( novembre 2017). Il me suffit de les tĂ©lĂ©charger pour pouvoir les utiliser. Pour cet exemple, jâai choisi un shapefile plutĂŽt petit : Alpine convention, qui comprend les pĂ©rimĂštres de la Convention alpine en Suisse.
Ces fichiers Ă©tant binaires et ne pouvant pas ĂȘtre consultĂ©s dans un Ă©diteur de texte, jâai besoin dâun outil pour consulter le fichier que je viens de tĂ©lĂ©charger.
PrĂ©sentation de QGIS, un systĂšme dâinformation gĂ©ographique gratuit et open source
QGIS est une solution complĂšte pour tous les types de SIG (SystĂšmes dâinformation gĂ©ographique). Il peut ĂȘtre tĂ©lĂ©chargĂ© et installĂ© gratuitement, et dispose de nombreux plugins et dâune communautĂ© active. Je vais lâutiliser pour regarder le shapefile que je viens de tĂ©lĂ©charger.
Pour cet exemple, jâai installĂ© QGIS avec le plugin OpenLayers. Voici Ă quoi il ressemble, une fois lancĂ©:
Pour obtenir des rĂ©fĂ©rences sur lâendroit oĂč je me trouve rĂ©ellement et oĂč vont mes donnĂ©es, jâajoute OpenStreetMap comme calque. Je fais Ă©galement un panoramique et un zoom sur la carte pour afficher la Suisse en gros plan.
LâĂ©tape suivante est dâouvrir rĂ©ellement le shapefile afin que QGIS puisse lâafficher sur la carte. Pour cela, il me suffit de double-cliquer sur le fichier .shp dans le navigateur Ă gauche.
Cette vue me donne dĂ©jĂ quelques informations sur le shapefile que je traite, sur la zone quâil couvre et sur le nombre de caractĂ©ristiques quâil contient. Le shapefile Alpine convention semble ne comporter quâune seule caractĂ©ristique, un grand polygone, ce qui est suffisant, Ă©tant donnĂ© quâil ne montre que les pĂ©rimĂštres de la Convention alpine en Suisse.
Pour afficher les zones et/ou les dĂ©tails quâil couvre, je peux modifier le style du calque, en le rendant transparent. Je fais Ă©galement un zoom pour voir si la forme est prĂ©cise. Ses bords doivent recouvrir exactement les frontiĂšres de la Suisse.
Parfait. Maintenant que je vois la forme de la caractĂ©ristique, je vais regarder les mĂ©tadonnĂ©es contenues dans le shapefile. Elles peuvent ĂȘtre consultĂ©es lorsque lâon ouvre le tableau des attributs du shapefile dans le navigateur situĂ© en bas Ă gauche.
Ce fichier ne contient pas non plus beaucoup de mĂ©tadonnĂ©es, mais je peux maintenant voir quâil contient en fait deux caractĂ©ristiques. Il pourrait y avoir des shapefiles plus intĂ©ressants Ă utiliser, mais je vais mâen tenir au sujet alpin.
Des avalanches et des bouquetins
En fouillant un peu plus dans le rĂ©pertoire de shapefiles dâOpenData, jâai trouvĂ© deux autres shapefiles qui pourraient fournir des donnĂ©es intĂ©ressantes: Distribution of ibex colonies (la rĂ©partition des colonies de bouquetins) et State of natural hazard mapping (Communes): Avalanches (la carte des zones avalancheuses).
MĂȘme si QGIS est un outil formidable pour examiner les donnĂ©es disponibles, je veux crĂ©er mon propre explorateur, peut-ĂȘtre mĂȘme Ă partir de plusieurs shapefiles et selon ma propre conception. Pour cela, il existe de multiples bibliothĂšques pour tous types de langages. Pour le dĂ©veloppement web, les trois bibliothĂšques les plus intĂ©ressantes seraient: PHP, Python et JavaScript.
Pour mon exemple, je vais crĂ©er une petite application front-end en JavaScript pour afficher mes trois shapefiles : la Convention alpine, les colonies de bouquetins et la cartographie des terrains avalancheux. Pour cela, je vais utiliser un package JavaScript tout simplement appelĂ© «shapefile» ainsi que leafletjs et afficher les polygones Ă partir des caractĂ©ristiques. Mais chaque chose en son temps.
Lecture des caractéristiques
Avis de non-responsabilitĂ© : les exemples de code suivants utilisent ES6 et impliquent une configuration webpack/babel. Ils ne peuvent pas ĂȘtre copiĂ©s/collĂ©s, mais ils montrent comment travailler avec les outils disponibles.
Tout dâabord, jâessaie de charger le shapefile de la convention alpine et de lâenregistrer dans la console. Le package shapefile que jâai mentionnĂ© est accompagnĂ© de deux exemples dans le README. Donc, pour simplifier, je me contente de cette approche :
import { open } from 'shapefile'
open('assets/shapefiles/alpine-convention/shapefile.shp').then(source => {
source.read().then(function apply (result) { // Read feature from the shapefile
if (result.done) { // If there's no features left, abort
return
}
console.log(result)
return source.read().then(apply) // Iterate over result
})
})
Cela fonctionne Ă merveille ! On obtient les deux caractĂ©ristiques du shapefile dans la console. Ce que je vois dĂ©jĂ ici, câest que les propriĂ©tĂ©s de chaque caractĂ©ristique, normalement stockĂ©es dans des fichiers sĂ©parĂ©s, sont dĂ©jĂ incluses dans la caractĂ©ristique. Câest le rĂ©sultat de la bibliothĂšque qui rĂ©cupĂšre les deux fichiers et les traite ensemble:
Maintenant, je regarde les coordonnĂ©es qui sont liĂ©es Ă la premiĂšre caractĂ©ristique. La premiĂšre chose qui se produit est lâaffichage de deux groupes diffĂ©rents, ce qui implique deux ensembles de coordonnĂ©es distincts, mais je vais ignorer le second pour le moment.
Et câest lĂ le premier piĂšge. Ces coordonnĂ©es ne ressemblent en rien au WGS84 (latitude/longitude), mais plutĂŽt Ă quelque chose dâautre. Les coordonnĂ©es en WGS84 devraient ĂȘtre un nombre Ă virgule flottante, commençant idĂ©alement par 47 et 8 pour la Suisse. Donc, ce shapefile mâa confrontĂ© Ă un systĂšme de rĂ©fĂ©rence de coordonnĂ©es diffĂ©rent (ou CRS). Afin dâafficher correctement la forme en haut dâune carte, ou de lâutiliser avec dâautres donnĂ©es, je vais dâabord convertir ces coordonnĂ©es en WGS84, mais pour ce faire, je dois dĂ©terminer quel CRS ce shapefile utilise. Puisque ce shapefile provient de lâOffice fĂ©dĂ©ral du dĂ©veloppement territorial ARE, le CRS utilisĂ© est trĂšs probablement CH1903, alias le système de coordonnées géographiques suisse.
Donc, pour faire la conversion, jâai besoin de faire des maths. En cherchant un peu sur le web, jâai trouvĂ© une solution JavaScript pour calculer les allers-retours entre CH1903 et WGS84. Mais je nâai besoin que de certaines parties, alors je copie et modifie un peu le code :
// Inspired by https://raw.githubusercontent.com/ValentinMinder/Swisstopo-WGS84-LV03/master/scripts/js/wgs84_ch1903.js
/**
* Converts CH1903(+) to Latitude
* @param y
* @param x
* @return {number}
* @constructor
*/
const CHtoWGSlat = (y, x) => {
// Converts military to civil and to unit = 1000km
// Auxiliary values (% Bern)
const yAux = (y - 600000) / 1000000
const xAux = (x - 200000) / 1000000
// Process lat
const lat = 16.9023892 +
(3.238272 * xAux) -
(0.270978 * Math.pow(yAux, 2)) -
(0.002528 * Math.pow(xAux, 2)) -
(0.0447 * Math.pow(yAux, 2) * xAux) -
(0.0140 * Math.pow(xAux, 3))
// Unit 10000" to 1" and converts seconds to degrees (dec)
return lat * 100 / 36
}
/**
* Converts CH1903(+) to Longitude
* @param y
* @param x
* @return {number}
* @constructor
*/
const CHtoWGSlng = (y, x) => {
// Auxiliary values (% Bern)
const yAux = (y - 600000) / 1000000
const xAux = (x - 200000) / 1000000
// Process lng
const lng = 2.6779094 +
(4.728982 * yAux) +
(0.791484 * yAux * xAux) +
(0.1306 * yAux * Math.pow(xAux, 2)) -
(0.0436 * Math.pow(yAux, 3))
// Unit 10000" to 1 " and converts seconds to degrees (dec)
return lng * 100 / 36
}
/**
* Convert CH1903(+) to WGS84 (Latitude/Longitude)
* @param y
* @param x
*/
export default (y, x) => [
CHtoWGSlat(y, x),
CHtoWGSlng(y, x)
]
Je peux maintenant lâutiliser dans mon application principale:
import { open } from 'shapefile'
import ch1903ToWgs from './js/ch1903ToWgs'
open('assets/shapefiles/alpine-convention/shapefile.shp').then(source => {
source.read().then(function apply (result) { // Read feature from the shapefile
if (result.done) { // If there's no features left, abort
return
}
// Convert CH1903 to WGS84
const coords = result.value.geometry.coordinates[0].map(coordPair => {
return ch1903ToWgs(coordPair[0], coordPair[1])
})
console.log(coords)
return source.read().then(apply) // Iterate over result
})
})
Ce qui donne les coordonnées ajustées suivantes:
Câest beaucoup mieux. Maintenant, je vais les superposer avec une carte pour obtenir quelque chose que je peux vraiment regarder.
Rendre les choses visibles
Pour cela, jâajoute leaflet Ă lâapplication avec les tuiles Positron (lite) de CartoDB. Le thĂšme Positron (lite) est gris clair/blanc, ce qui offre un bon contraste avec les caractĂ©ristiques que je vais afficher par-dessus. OpenStreetMap est gĂ©nial mais trop colorĂ©, ce qui fait que les polygones ne sont pas suffisamment visibles.
import leaflet from 'leaflet'
import { open } from 'shapefile'
import ch1903ToWgs from './js/ch1903ToWgs'
/**
* Add the map
*/
const map = new leaflet.Map(document.querySelector('#map'), {
zoomControl: false, // Disable default one to re-add custom one
}).setView([46.8182, 8.2275], 9) // Show Switzerland by default
// Move zoom control to bottom right corner
map.addControl(leaflet.control.zoom({position: 'bottomright'}))
// Add the tiles
const tileLayer = new leaflet.TileLayer('//cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
minZoom: 9,
maxZoom: 20,
attribution: '© CartoDB basemaps'
}).addTo(map)
map.addLayer(tileLayer)
/**
* Process the shapefile
*/
open('assets/shapefiles/alpine-convention/shapefile.shp').then(source => {
source.read().then(function apply (result) { // Read feature from the shapefile
if (result.done) { // If there's no features left, abort
return
}
// Convert CH1903 to WGS84
const coords = result.value.geometry.coordinates[0].map(coordPair => {
return ch1903ToWgs(coordPair[0], coordPair[1])
})
console.log(coords)
return source.read().then(apply) // Iterate over result
})
})
Jâobtiens dĂ©jĂ une belle carte avec laquelle je peux travailler:
LâĂ©tape suivante consiste Ă connecter la carte au chargement du shapefile. Pour cela, je crĂ©e des polygones leaflet Ă partir des coordonnĂ©es que jâai prĂ©cĂ©demment transformĂ©es en WGS84:
// ...
// Convert CH1903 to WGS84
const coords = result.value.geometry.coordinates[0].map(coordPair => {
return ch1903ToWgs(coordPair[0], coordPair[1])
})
const leafletPolygon = new leaflet.Polygon(coords, {
weight: 0.5,
color: '#757575',
fillOpacity: 0.3,
opcacity: 0.3,
})
leafletPolygon.addTo(map)
// ...
Et je regarde le résultat:
Sympa!
Le résultat final
Avec un peu plus dâinterface utilisateur·trice et lâajout dâun bouton Ă bascule pour les multiples shapefiles prĂ©alablement tĂ©lĂ©chargĂ©s, je peux crĂ©er une petite application qui me montre les zones Ă risque dâavalanche et les territoires oĂč vivent les bouquetins en Suisse :
Nous pouvons voir les deux shapefiles en action : les polygones vert clair, vert, jaune et rouge sont des communes prĂ©sentant un certain risque dâavalanche (vert clair = faible, vert = moyen-faible, jaune = moyen, rouge = Ă©levĂ©), les polygones plus foncĂ©s en haut reprĂ©sentent les colonies de bouquetins.
Cette carte offre dĂ©jĂ un bon aperçu du comportement des bouquetins : apparemment, ils vivent seulement dans des zones Ă risque dâavalanche faible Ă moyen-faible. Ils ont donc tendance Ă Ă©viter les zones Ă risque moyen et Ă©levĂ©. Ătant donnĂ© que les bouquetins prĂ©fĂšrent les terrains alpins et que ces terrains prĂ©sentent gĂ©nĂ©ralement des risques dâavalanches, cette affirmation est plausible. Je dispose maintenant de donnĂ©es visibles qui la confirment.
Lâapplication terminĂ©e est disponible ici, et son code source se trouve dans ce répertoire.
Les piĂšges
Bien que les shapefiles soient un moyen formidable de traiter les donnĂ©es SIG, il y a quelques piĂšges Ă Ă©viter. Lâun dâentre eux, que jâai dĂ©jĂ mentionnĂ©, est un CRS inattendu. Dans la plupart des cas, il peut ĂȘtre identifiĂ© au point dâutilisation, mais quand on consulte un shapefile, il est recommandĂ© de vĂ©rifier quel CRS il utilise. Le deuxiĂšme piĂšge Ă Ă©viter est la taille des shapefiles. Lors de lâutilisation de certains shapefiles avec JavaScript directement dans le navigateur, ce dernier peut « planter » car il tente de traiter la quantitĂ© astronomique de polygones. Il existe cependant des solutions : on peut soit simplifier le shapefile en supprimant les polygones inutiles, soit le prĂ©traiter et stocker ses polygones dans une sorte de base de donnĂ©es et nâinterroger que les polygones actuellement visibles.
ĂlĂ©ments Ă retenir
Personnellement, jâadore travailler avec les shapefiles. Les domaines dâutilisation sont nombreux et lâexploration des donnĂ©es quâils contiennent est vraiment passionnante. MĂȘme sâil peut y avoir un ou deux piĂšges, en gĂ©nĂ©ral, câest plaisant, car on peut obtenir quelque chose dâabouti sans trop dâefforts. La communautĂ© OpenData utilise beaucoup les shapefiles, câest donc une norme bien Ă©tablie. Il existe aussi un grand nombre de bibliothĂšques et dâoutils qui rendent le travail avec les shapefiles vraiment gĂ©nial.