Dans un contexte d’essor de carnets interactifs (notebooks), l’objet de cet atelier consiste à montrer les possibilités offertes par Quarto, un système de publication scientifique et technique open source basé sur Pandoc, qui permet d’articuler différents langages pour réaliser des géovisualisations reproductibles dans un seul et même document. Cet atelier proposera aux participants une prise en main des principales fonctionnalités offertes par Quarto, puis une mise en œuvre de chaînes de traitements de l’information géographique reproductibles avec R, Python et Observable JavaScript dans cet environnement.
Déroulé de la demi-journée
Installation des logiciels nécessaires à la mise en place de l’environnement de développement (Quarto, R, Python, RStudio, etc.).
Présentation et prise en main des principales fonctionnalités des notebooks Quarto. Présentation des écosystèmes spatiaux de R et Python, et, d’un jeu de données commun sur lequel s’appuiera l’ensemble de l’atelier.
Traitement des données vectorielles : Mise en pratique d’une chaîne de traitements reproductible avec R, notamment grâce aux packages sf et mapsf.
Traitement des données raster : Mise en pratique d’une chaîne de traitements reproductible avec Python (bibliothèques geopandas, rasterio, xarray, matplotlib, etc.).
Création de visualisations cartographiques interactives avec ojs (bibliothèques bertin, Plot, Gridviz, Deck.gl).
Export du document quarto produit pendant l’atelier (templates de différentes revues scientifiques, slides, https://quartopub.com/, etc.).
Toutes les données mobilisées dans cet atelier son stockées dans un projet RStudio. Vous devez le télécharger puis le décompresser sur votre machine. Il vous sera ensuite possible de tester l’ensemble des manipulations proposées dans ce document au sein du projet quebec. Télécharger le projet
Préparation des données
Dans cet atelier nous allons utiliser des données portant sur les municipalités du Québec. Dans un premier temps nous avons préparé et nettoyé les données avec le logiciel R. Les différentes étapes de préparation sont décrites dans le script suivant.
Code
library(sf)library(mapsf)library(readxl)library(rmapshaper)# Communesmun <-st_read("data-raw/munic_s.shp", quiet =TRUE)# Transformation conique équivalente d'ALbersmun <-st_transform(mun, 6623)# Suppression des surfaces en eaumun <- mun[!is.na(mun$MUS_CO_GEO),]# Selection des variables pertinentesmun <- mun[, c("MUS_CO_GEO", "MUS_NM_MUN", "MUS_CO_MRC", "MUS_NM_MRC", "MUS_NM_REG")]# Simplifier les contours des polygonesmun <-ms_simplify(mun, )# Agregation des multipolygonesmun <-aggregate(x = mun[, -1], by =list(MUS_CO_GEO = mun$MUS_CO_GEO),FUN = head, 1)mun <-st_cast(mun, "MULTIPOLYGON")# Import données population et nettoyagedf <-read_xlsx("data-raw/stats_socio_eco/Pop-MUN-total.xlsx", skip =3, n_max =1292,col_types =c(rep("text", 2),"numeric",rep("text", 4), rep("numeric", 22))) df <-as.data.frame(df)df <- df[,c(1:3, 8:29)]col_pop <-paste0("POP_", seq(2001, 2022, 1))colnames(df) <-c("NOM", "STATUT", "MUS_CO_GEO", col_pop)df$MUS_CO_GEO <-formatC(df$MUS_CO_GEO, width =5, flag ="0", format ="d")mun <-merge(mun, df, by ="MUS_CO_GEO", all.x = T)# Import données socio-éco et nettoyagedf2 <-data.frame(read_xlsx("data-raw/stats_socio_eco/Tableau.xlsx", skip =4, n_max =1163))df2 <- df2[-c(1:2),]df2 <- df2[,c(2:4,6,8,10)]colnames(df2) <-c("NOM", "REG", "VIT_ECO", "REV_MED", "TX_2564", "TAAM")df2$id <-paste0(df2$NOM, "_", df2$REG)df2$VIT_ECO <-as.numeric(df2$VIT_ECO)df2$REV_MED <-as.numeric(df2$REV_MED)df2$TX_2564 <-as.numeric(df2$TX_2564)df2$TAAM <-as.numeric(df2$TAAM)mun$id <-paste0(mun$NOM, " (", mun$STATUT, ")_", mun$MUS_NM_REG)mun <-merge(mun, df2, by ="id", all.x =TRUE)mun <- mun[, c(2:6, 8:30, 33:36)]# agregation de municipalité en Municipalité régionale de comté (MRC)mun <-st_set_precision(mun, 1)mrc <-aggregate(mun[, c("MUS_NM_MRC","MUS_NM_REG")], by =list(MUS_CO_MRC = mun$MUS_CO_MRC),FUN = head, 1)mrc <-st_cast(mrc, "MULTIPOLYGON")mun <-st_set_precision(mun, 0)st_write(st_transform(mrc, 4326), "data/mrc.geojson")st_write(st_transform(mun, 4326), "data/munic.geojson")
Ce script abouti à la création de deux fichiers :
mrc.geojson est un fond de carte des Municipalité Régionales de Comté.
munic.geojson et un fond de carte des municipalités contenant des données socio-démographiques.
R
L’écosystème spatial de R
Le logiciel R (R Core Team, 2023) permet depuis longtemps de traiter et représenter les données spatiales.
Le principal package permettant de traiter les données vectorielles avec R est le package sf(Pebesma, 2018).
Les packages stars(Pebesma et Bivand, 2023) et terra(Hijmans, 2023) quand à eux sont les principaux packages dédiés au traitement des données raster.
Les développements actuels s’appuient sur ce socle et forment un écosystème robuste qui offre aux utilisateurs la plupart des fonctionnalités autrefois réservées aux Systèmes d’Information Géographique.
Le package mapsf(Giraud, 2022a) par exemple s’appuie sur le package sf pour créer des cartes thématiques. D’autres packages sont dédiés à la cartographie interactive, aux statistiques spatiales, l’interaction avec des API de cartographie, la création de cartogrammes etc.
Le package sf
Le package sf a été publié fin 2016 par Edzer Pebesma. Ce package permet l’import, l’export, la manipulation et l’affichage de données spatiales vectorielles. Pour cela sf s’appuie sur une série de bibliothèques spatiales : GDAL (GDAL/OGR contributors, 2022) et PROJ (PROJ contributors, 2021) pour les opérations d’import, d’export et de projection, et GEOS (GEOS contributors, 2021) pour les opérations de géotraitement (buffer, intersection…).
Ce package propose des objets simples (suivant le standard simple feature) dont la manipulation est assez aisée. Une attention particulière a été portée à la compatibilité du package avec la syntaxe pipe (|> ou %>%) et les opérateurs du tidyverse(Wickham et al., 2019).
Les objets sf sont des data.frame dont l’une des colonnes contient des géométries.
Ce format est très pratique dans la mesure ou les données et les géométries sont intrinsèquement liées dans un même objet.
mapsf permet de créer la plupart des types de carte utilisés habituellement en cartographie statistique.
Pour chaque type de carte, plusieurs paramètres permettent de personnaliser la représentation cartographique. Ces paramètres sont les mêmes que ceux que l’on retrouve dans les logiciels de SIG ou de cartographie usuels.
Associées aux fonctions de représentation des données d’autres fonctions sont dédiées à l’habillage cartographique, à la création de cartons ou à l’export des cartes. mapsf est le successeur de cartography(Giraud et Lambert, 2017), il offre les mêmes fonctionnalités principales tout en étant plus léger et plus ergonomique.
library(mapsf)# Import the sample datasetmtq <-mf_get_mtq()# Plot the base mapmf_map(x = mtq)# Plot proportional symbolsmf_map(x = mtq, var ="POP", type ="prop")# Plot a map layoutmf_layout(title ="Population in Martinique", credits ="T. Giraud; Sources: INSEE & IGN, 2018")
# Export a map with a theme and extra margins mf_export(x = mtq, filename ="fig/mtq.svg", width =5, theme ="green", expandBB =c(0,0,0,.3)) # Plot a shadowmf_shadow(mtq, col ="grey10", add =TRUE)# Plot a choropleth mapmf_map(x = mtq, var ="MED", type ="choro",pal ="Dark Mint", breaks ="quantile", nbreaks =6, leg_title ="Median Income\n(euros)", leg_val_rnd =-2, add =TRUE)# Start an inset mapmf_inset_on(x ="worldmap", pos ="right")# Plot mtq position on a worldmapmf_worldmap(mtq, col ="#0E3F5C")# Close the insetmf_inset_off()# Plot a titlemf_title("Wealth in Martinique, 2015")# Plot creditsmf_credits("T. Giraud\nSources: INSEE & IGN, 2018")# Plot a scale barmf_scale(size =5)# Plot a north arrowmf_arrow('topleft')dev.off()
Note
D’autres packages peuvent être utilisés pour réaliser des cartes thématiques.
Le package ggplot2(Wickham, 2016), en association avec le package ggspatial(Dunnington, 2023), permet par exemple d’afficher des objets spatiaux et de réaliser des cartes thématiques simples.
Le package tmap(Tennekes, 2018) est dédié à la création de cartes thématiques, il utilise une syntaxe proche de celle de ggplot2 (enchaînement d’instructions combinées avec le signe +).
La documentation et les tutoriels pour utiliser ces deux packages sont facilement accessibles sur le web.
Nous utilisons le package sf pour importer 2 fichiers geojson.
Il est ensuite possible de changer la projection des couches avec la fonction st_transform().
Nous pouvons aussi créer une couche des régions du Québec en agrégeant les Municipalité Régionales de Comté.
# Import des donnéeslibrary(sf)mun_raw <-st_read("data/munic.geojson", quiet=TRUE)mrc_raw <-st_read("data/mrc.geojson", quiet =TRUE)# Projection NAD83 / Quebec Albersepsg_6623 <-"EPSG:6623"mun <-st_transform(mun_raw, epsg_6623)mrc <-st_transform(mrc_raw, epsg_6623)# Création d'une couche des régionsreg <-aggregate(x = mrc[,'MUS_NM_REG'], by =list(REG = mrc$MUS_NM_REG), FUN = head, 1)
Première cartographie simple
Sur cette première carte nous allons simplement afficher les différentes couches en les superposant.
Nous utilisons le package mapsf qui permet aussi de créer une couche d’étiquettes avec la fonction mf_label().
library(mapsf)mf_map(mun, border ="white")mf_map(mrc, col =NA, border ="grey70", lwd =1, add =TRUE)mf_map(reg, col =NA, border ="black", lwd =1.2, add =TRUE)mf_label(reg, var ="MUS_NM_REG", halo =TRUE, overlap =FALSE)mf_scale(size =100)mf_title("Le Québec")mf_credits(txt ="Ministère des Ressources naturelles et des Forêts, 2023", bg ="#ffffffCC")
Où sont les restaurants dans la ville de Québec?
Nous voulons savoir dans quelles zones de la ville de Québec se trouvent les restaurants.
Pour connaître la position des restaurants dans la ville nous allons extraire des informations la base de donnés cartographique libre OpenStreetMap avec le package osmdata(Padgham et al., 2017). Le package osmdata utilise l’API du service Overpass turbo pour extraire des données vectorielles de la base de données OpenStreetMap.
Nous devons tout d’abord définir une emprise spatiale à partir de laquelle nous pourrons extraire les informations sur les restaurants.
Après avoir démarré les packages nécessaires, nous allons tout d’abord sélectionner la MRC de Québec dans la couche des MRC.
Nous allons ensuite définir une bounding box correspondant à l’emprise de la MRC. Le package osmdata fonctionne avec des objets en WGS84 nous avons donc besoin de reprojeter les données.
# Reprojection en WGS84target_4326 <-st_transform(target, "EPSG:4326")# Création de la bounding boxbbox <-st_bbox(target_4326)
Nous pouvons maintenant définir ce que nous voulons extraire de la base de donnée OSM. Une connaissance de la structuration de la base de données OSM est nécessaire (Wiki OpenStreetMap). Nous utilisons le système de clef/valeur pour construire la requête.
# Initialisation de la requête à envoyerq <-opq(bbox =st_bbox(bbox))# Choix des objets à extrairereq <-add_osm_feature(opq = q, key ='amenity', value ="restaurant")
La fonction osmdata_sf() permet d’envoyer la requête et de recevoir les résultats sous forme de data.framesf.
# Reception de la requeteres <-osmdata_sf(q = req)res
Object of class 'osmdata' with:
$bbox : 46.7152971489633,-71.5896138558637,46.9818615959766,-71.1448958492775
$overpass_call : The call submitted to the overpass API
$meta : metadata including timestamp and version numbers
$osm_points : 'sf' Simple Features Collection with 859 points
$osm_lines : NULL
$osm_polygons : 'sf' Simple Features Collection with 61 polygons
$osm_multilines : NULL
$osm_multipolygons : NULL
Ce résultat contient des restaurants représentés par de points (osm_points) et d’autre représentés par des polygones (osm_polygons). La couche des points contient également les points qui composent les polygones de la couche de polygones. La fonction unique_osmdata() permet de filtrer ces “doublons”.
# Reduction du resultats # (les points composant les polygones sont supprimés)res <-unique_osmdata(res)res
Object of class 'osmdata' with:
$bbox : 46.7152971489633,-71.5896138558637,46.9818615959766,-71.1448958492775
$overpass_call : The call submitted to the overpass API
$meta : metadata including timestamp and version numbers
$osm_points : 'sf' Simple Features Collection with 407 points
$osm_polygons : 'sf' Simple Features Collection with 61 polygons
$osm_multilines : NULL
$osm_multipolygons : NULL
L’étape suivante consiste à extraire les points et les polygones, puis à transformer les polygones en points en utilisant leurs centroides. Cela permet de constituer une couche de points complète et cohérente.
# Extraction des pointsresto_point <- res$osm_points# Extraction des polygonesresto_poly <- res$osm_polygons# Transformation des polygones en points (centroides)resto_poly_point <-st_centroid(resto_poly)# Listes des champs à conserverchps <-c("osm_id", "name", "cuisine")# Rassembler les 2 couches de pointsresto <-rbind(resto_point[, chps], resto_poly_point[, chps])resto
Simple feature collection with 468 features and 3 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: -71.54201 ymin: 46.71551 xmax: -71.14547 ymax: 46.93504
Geodetic CRS: WGS 84
First 10 features:
osm_id name cuisine
291667452 291667452 Chez Cora <NA>
295803248 295803248 Normandin <NA>
345886369 345886369 Chez Victor burger
386267989 386267989 Ly-Hai vietnamese
393559700 393559700 Mike's italian
400282934 400282934 Le Rideau Rouge burger
400283204 400283204 Petits Creux & Grands Crus french
400283356 400283356 Pizzédelic pizza
400292015 400292015 Piazzetta pizza
400292075 400292075 Graffiti <NA>
geometry
291667452 POINT (-71.19642 46.85348)
295803248 POINT (-71.41531 46.85531)
345886369 POINT (-71.22581 46.80766)
386267989 POINT (-71.2281 46.80561)
393559700 POINT (-71.35824 46.83807)
400282934 POINT (-71.22516 46.80328)
400283204 POINT (-71.2253 46.80341)
400283356 POINT (-71.22521 46.80333)
400292015 POINT (-71.22463 46.80285)
400292075 POINT (-71.22447 46.80271)
Maintenant que nous avons cette couche de restaurants nous allons pouvoir l’afficher.
Pour cela nous pouvons utiliser un fond de carte issues de tuiles raster grâce au package maptiles(Giraud, 2023). Nous allons d’abord reprojeter les différentes couches d’information en Web/Mercator (EPSG:3857) car les tuiles raster sont fournies dans cette projection .
Nous allons maintenant agréger les restaurants dans une grille régulière pour tenter rendre plus lisible l’organisation spatiale des restaurants à Québec.
Nous pouvons aussi utiliser une méthode de lissage telle que la méthode de lissage par noyaux (KDE) grâce au package spatstat(Baddeley et al., 2015).
library(spatstat)library(terra)library(tanaka)p <-as.ppp(st_coordinates(resto), W =as.owin(st_bbox(target)))# portée du lissage 500mds <-density.ppp(p, sigma =500, eps =c(100, 100), positive =TRUE)# transformation en raster terra et passage en unité par km²rasdens <-rast(ds) *1000*1000# ajout des information de projectioncrs(rasdens) <-'EPSG:3857'# affichage par défautplot(rasdens)
Il est ensuite possible de représenter les densités calculées avec les packages tanaka(Giraud, 2022b) et mapsf.
Un projet open source pour faciliter le travail avec des données géospatiales vectorielles en Python. GeoPandas étend Pandas pour permettre de disposer d’un type de colonne géométrique et pour permettre d’effectuer des opérations spatiales. Les opérations géométriques sont réalisées avec shapely, les accès en lecture / écriture aux fichiers utilisent fiona et la visualisation utilise matplotlib.
xarray pour travailler avec des tableaux multidimensionnels étiquetés
rioxarray pour ouvrir des jeux des données raster avec rasterio, les stocker dans le format de xarray, et avoir accès à différentes fonctionnalités de rasterio
moins “rustique” que d’utiliser rasterio seul / dépend de l’usage souhaité
Le package Rasterio
Exemple d’utilisation :
import rasterio as riowith rio.open('./data/MODIS_ARRAY.nc') as f:# Métadonnées : metadata = f.meta# Lire toutes les bandes : data = f.read()# Ou f.read(1) pour lire seulement la première bande
Un dictionnaire de métadonnées + un tableau NumPy contenant la (ou les) bande(s) :
Binding Python pour GRASS + Intégration dans les notebooks Jupyter
Iris - Analyse et visualisation de données des sciences de la Terre.
Pour la cartographie : cartopy ; geoviews et geoplot basés sur cartopy ; PyGMT (selon les usages - pas de solution aussi aboutie que mapsf pour les cartes réalisées en SHS).
emp_id emp_name salary start_date
1 0 Rick 623.30 2012-01-01 01:00:00
2 1 Dan 515.20 2013-09-23 02:00:00
3 2 Michelle 611.00 2014-11-15 01:00:00
4 3 Ryan 729.00 2014-05-11 02:00:00
5 4 Jane 843.25 2015-03-27 01:00:00
print(py$arr)
[,1] [,2]
[1,] 12 47
[2,] 34 90
[3,] 23 19
Interaction R ⇄ Python dans Quarto (suite)
Et pour les objets spatiaux ? (sf / geopandas)
Si la conversion data.frame ⇄ DataFrame est transparente, il n’en est pas de même pour les dataframes avec une composante spatiale (les objets sf sont convertis en DataFrameau lieu de GeoDataFrame : perte de la dimension spatiale, attribut crs non transféré et colonne geometry qui contient désormais seulement des listes de coordonnées).
L’écriture d’une petite function permet de résoudre ça :
Utilisation des widgets Jupyter possible (seulement si utilisation de l’engin de rendu jupyter, pas avec knitr - i.e. l’inverse des htmlwidgets en R qui ne fonctionne que si knitr est utilisé).
Objectifs : Récupérer les données préparées avec R (MRC et localisation des restaurants) pour les croiser avec des données d’occupation du sol au format raster.
Les imports nécessaires :
import numpy as npimport pandas as pdimport geopandas as gpdimport rioxarrayimport xarrayfrom matplotlib import pyplot as pltfrom rasterio.plot import showfrom rasterstats import zonal_stats, point_query
Récupération des jeux de données et de quelques autres infos (SRC, etc.) depuis l’environnement R :
# Le SCR des donnéesdst_crs = r.epsg_6623# On récupère le jeu de données "mrc" (municipalités régionales de comté)# et le jeu de données "resto"mrc = restore_geodataframe(r.mrc, dst_crs, "MultiPolygon")resto = restore_geodataframe(r.resto, "EPSG:3857", "Point")resto.to_crs(dst_crs, inplace=True)reg = mrc.dissolve(by="MUS_NM_REG")sel = r.nom_mrc # Québec# Chemin du jeu de données raster# et du fichier contenant les descriptions des catégorieslc_fp ='./data/T01_PROVINCE.tif'lc_categories_fp ='./data/correspondance_raster_CL_COUV.dbf'
Sélection des entités correspondantes et affichage, relativement au reste de la province :
Affichons le raster reprojeté et l’emprise de la ville de Québec :
# Remplacer les '255' par '0'xds.data[xds.data ==255] =0# Étendue des donnéesbounds = xds.rio.bounds()extent = [bounds[0], bounds[2], bounds[1], bounds[3]]# Création d'une figure videfig, ax = plt.subplots(figsize=(16, 12))# Affichage du raster (attribut 'data' de l'objet xds)show(xds.data[0], ax=ax, extent=extent, cmap="Set3")# Affichage de la ville de Québec en rouge par dessusextract.plot(ax=ax, color='red', edgecolor='red', linewidth=2)# On enlève l'affichage des axesax.set_axis_off()ax
Ouverture du fichier DBF qui contient les correspondances entre les codes et les noms des catégories d’utilisation des sols :
ID CL_COUV Descriptio
0 1.0 010000 Surfaces artificielles
1 2.0 020000 Terres agricoles
2 3.0 060000 Milieux humides forestiers
3 4.0 070000 Milieux humides herbacés ou arbustifs
4 5.0 100000 Plans et cours d’eau intérieure
5 6.0 110101 Forêts de conifères à couvert fermé
6 7.0 110102 Forêts de feuillus à couvert fermé
7 8.0 110103 Forêts mixtes à couvert fermé
8 9.0 110200 Forêts à couvert ouvert
9 10.0 000000 Pas de données
10 11.0 000001 En attente de traitement
# On va convertir cette DataFrame en un dictionnaire pour l'utiliser# comme un mapping ID -> Descriptioncategories = {int(k): v['Descriptio']for k,v in categories[['ID', 'Descriptio']].set_index('ID').to_dict(orient='index').items()}# Bien qu'il y ait une catégorie 'Pas de données' (ID 10)# certaines cellules ne comportent pas de valeur (nodata, ici avec la valeur 0)categories[0] ='Pas de données'categories
{1: 'Surfaces artificielles', 2: 'Terres agricoles', 3: 'Milieux humides forestiers', 4: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 5: 'Plans et cours d’eau intérieure', 6: 'Forêts de conifères à couvert fermé', 7: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 8: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 9: 'Forêts à couvert ouvert', 10: 'Pas de données', 11: 'En attente de traitement', 12: 'Surfaces artificielles vers terres agricoles', 13: 'Surfaces artificielles vers milieux humides forestiers', 14: 'Surfaces artificielles vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 15: 'Surfaces artificielles vers plans et cours d’eau intérieure', 16: 'Surfaces artificielles vers forêts de conifères à couvert fermé', 17: 'Surfaces artificielles vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 18: 'Surfaces artificielles vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 19: 'Surfaces artificielles vers forêts à couvert ouvert', 20: 'Surfaces artificielles vers pas de données', 21: 'Surfaces artificielles vers en attente de traitement', 22: 'Terres agricoles vers surfaces artificielles', 23: 'Terres agricoles vers milieux humides forestiers', 24: 'Terres agricoles vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 25: 'Terres agricoles vers plans et cours d’eau intérieure', 26: 'Terres agricoles vers forêts de conifères à couvert fermé', 27: 'Terres agricoles vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 28: 'Terres agricoles vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 29: 'Terres agricoles vers forêts à couvert ouvert', 30: 'Terres agricoles vers pas de données', 31: 'Terres agricoles vers en attente de traitement', 32: 'Milieux humides forestiers vers surfaces artificielles', 33: 'Milieux humides forestiers vers terres agricoles', 34: 'Milieux humides forestiers vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 35: 'Milieux humides forestiers vers plans et cours d’eau intérieure', 36: 'Milieux humides forestiers vers forêts de conifères à couvert fermé', 37: 'Milieux humides forestiers vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 38: 'Milieux humides forestiers vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 39: 'Milieux humides forestiers vers forêts à couvert ouvert', 40: 'Milieux humides forestiers vers pas de données', 41: 'Milieux humides forestiers vers en attente de traitement', 42: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers surfaces artificielles', 43: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers terres agricoles', 44: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers milieux humides forestiers', 45: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers plans et cours d’eau intérieure', 46: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers forêts de conifères à couvert fermé', 47: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 48: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 49: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers forêts à couvert ouvert', 50: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers pas de données', 51: 'Milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs vers en attente de traitement', 52: 'Plans et cours d’eau intérieure vers surfaces artificielles', 53: 'Plans et cours d’eau intérieure vers terres agricoles', 54: 'Plans et cours d’eau intérieure vers milieux humides forestiers', 55: 'Plans et cours d’eau intérieure vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 56: 'Plans et cours d’eau intérieure vers forêts de conifères à couvert fermé', 57: 'Plans et cours d’eau intérieure vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 58: 'Plans et cours d’eau intérieure vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 59: 'Plans et cours d’eau intérieure vers forêts à couvert ouvert', 60: 'Plans et cours d’eau intérieure vers pas de données', 61: 'Plans et cours d’eau intérieure vers en attente de traitement', 62: 'Forêts de conifères à couvert fermé vers surfaces artificielles', 63: 'Forêts de conifères à couvert fermé vers terres agricoles', 64: 'Forêts de conifères à couvert fermé vers milieux humides forestiers', 65: 'Forêts de conifères à couvert fermé vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 66: 'Forêts de conifères à couvert fermé vers plans et cours d’eau intérieure', 67: 'Forêts de conifères à couvert fermé vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 68: 'Forêts de conifères à couvert fermé vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 69: 'Forêts de conifères à couvert fermé vers forêts à couvert ouvert', 70: 'Forêts de conifères à couvert fermé vers pas de données', 71: 'Forêts de conifères à couvert fermé vers en attente de traitement', 72: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers surfaces artificielles', 73: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers terres agricoles', 74: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers milieux humides forestiers', 75: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 76: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers plans et cours d’eau intérieure', 77: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers forêts de conifères à couvert fermé', 78: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 79: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers forêts à couvert ouvert', 80: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers pas de données', 81: 'Forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé vers en attente de traitement', 82: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers surfaces artificielles', 83: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers terres agricoles', 84: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers milieux humides forestiers', 85: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 86: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers plans et cours d’eau intérieure', 87: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers forêts de conifères à couvert fermé', 88: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 89: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers forêts à couvert ouvert', 90: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers pas de données', 91: 'Forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé vers en attente de traitement', 92: 'Forêts à couvert ouvert vers surfaces artificielles', 93: 'Forêts à couvert ouvert vers terres agricoles', 94: 'Forêts à couvert ouvert vers milieux humides forestiers', 95: 'Forêts à couvert ouvert vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 96: 'Forêts à couvert ouvert vers plans et cours d’eau intérieure', 97: 'Forêts à couvert ouvert vers forêts de conifères à couvert fermé', 98: 'Forêts à couvert ouvert vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 99: 'Forêts à couvert ouvert vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 100: 'Forêts à couvert ouvert vers pas de données', 101: 'Forêts à couvert ouvert vers en attente de traitement', 102: 'Pas de données vers surfaces artificielles', 103: 'Pas de données vers terres agricoles', 104: 'Pas de données vers milieux humides forestiers', 105: 'Pas de données vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 106: 'Pas de données vers plans et cours d’eau intérieure', 107: 'Pas de données vers forêts de conifères à couvert fermé', 108: 'Pas de données vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 109: 'Pas de données vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 110: 'Pas de données vers forêts à couvert ouvert', 111: 'Pas de données vers en attente de traitement', 112: 'En attente de traitement vers surfaces artificielles', 113: 'En attente de traitement vers terres agricoles', 114: 'En attente de traitement vers milieux humides forestiers', 115: 'En attente de traitement vers milieux\xa0humides\xa0herbacés\xa0ou\xa0arbustifs', 116: 'En attente de traitement vers plans et cours d’eau intérieure', 117: 'En attente de traitement vers forêts de conifères à couvert fermé', 118: 'En attente de traitement vers forêts\xa0de\xa0feuillus\xa0à\xa0couvert\xa0fermé', 119: 'En attente de traitement vers forêts\xa0mixtes\xa0à\xa0couvert\xa0fermé', 120: 'En attente de traitement vers forêts à couvert ouvert', 121: 'En attente de traitement vers pas de données', 0: 'Pas de données'}
Calcul des statistiques zonales (combien de cellules de chaque type dans la sélection) :
Traçons un histogramme de ce résultat, en utilisant les noms appropriés des catégories d’utilisation des sols :
# Trier les valeurs par le nombre de cellulesorted_values =sorted(stats[0].items(), key =lambda x: x[1], reverse=True)# Utilise les noms de catégories du DataFrame `categories`...x = [categories[int(k)] for k, _ in sorted_values]# ...et les valeurs que nous venons de calculer avec la fonction `zonal_stats`height = [v for _, v in sorted_values]fig, ax = plt.subplots(figsize=(8, 8))bar_container = ax.bar(x, height)ax.set_xticklabels( ax.get_xticklabels(), rotation=45, horizontalalignment='right')fig.tight_layout()ax
Dans quel type d’occupation du sol se trouvent les restaurants précédemment récupérés ?
geoms = resto['geometry'].tolist()resto["lc"] = [categories[int(x)] for x in point_query(vectors=geoms, raster=xds.data[0], nodata=0, affine=xds.rio.transform())]resto.head()
osm_id name cuisine geometry lc
291667452 291667452 Chez Cora NA POINT (-205284.124 320601.265) Surfaces artificielles
295803248 295803248 Normandin NA POINT (-221931.609 321452.378) Surfaces artificielles
345886369 345886369 Chez Victor burger POINT (-207712.728 315587.610) Surfaces artificielles
386267989 386267989 Ly-Hai vietnamese POINT (-207895.478 315365.940) Surfaces artificielles
393559700 393559700 Mike's italian POINT (-217665.483 319360.818) Surfaces artificielles
Quels restaurants ne sont pas situés dans une zone artificialisée ?
selection = resto[(resto.lc !='Surfaces artificielles') & (resto.lc !='Pas de données')]selection
osm_id name cuisine geometry lc
2395695353 2395695353 Yuzu sushi sushi POINT (-222424.372 321631.681) Milieux humides forestiers
3089591298 3089591298 Café 47 NA POINT (-211438.971 309903.333) Milieux humides forestiers
3222747115 3222747115 Yuzu Sushi NA POINT (-213715.133 318403.631) Forêts de feuillus à couvert fermé
3224167888 3224167888 Le Subtil NA POINT (-212550.597 319033.410) Forêts de feuillus à couvert fermé
3257077054 3257077054 Ben & Florentine NA POINT (-221125.085 321891.351) Milieux humides forestiers
3801921158 3801921158 Le Commandant italian POINT (-218914.348 315176.377) Forêts mixtes à couvert fermé
4726892021 4726892021 Pidz pizza POINT (-229413.591 325273.771) Plans et cours d’eau intérieure
5099196285 5099196285 Le Bistro Évolution NA POINT (-210863.848 307329.858) Forêts mixtes à couvert fermé
5485394052 5485394052 érablière du chemin du Roy maple;ice_cream;quebec;canadian POINT (-222402.680 308460.059) Milieux humides forestiers
5485401715 5485401715 Quai 1636 regional;french POINT (-216802.399 309187.074) Terres agricoles
6643475285 6643475285 La cage NA POINT (-203039.864 313768.119) Milieux humides herbacés ou arbustifs
7816042184 7816042184 Chez Victor NA POINT (-210673.674 307668.201) Terres agricoles
10878153356 10878153356 Pacini italian POINT (-213819.005 318251.978) Forêts de conifères à couvert fermé
10878153357 10878153357 Yuzu sushi sushi POINT (-213801.047 318254.188) Forêts de feuillus à couvert fermé
189982784 189982784 Resto-Pub Ryna NA POINT (-211598.938 306756.046) Forêts de feuillus à couvert fermé
998906150 998906150 Benny & Co. chicken;barbecue;poutine POINT (-213983.922 325706.559) Terres agricoles
Objectif : Comparer l’occupation du sol de la MRC de Trois-Rivières à l’occupation du sol de la MRC de Montréal.
Vous cherchez à obtenir :
un diagramme en barre (combinant le résultat des statistiques zonales pour la MRC de Trois-Rivières et pour la MRC de Montréal),
un résumé (pourquoi pas en HTML !) de la surface de chaque type d’occupation du sol, comme celui présenté en exemple ci-dessous, pour Trois-Rivières et Montréal.
Au début de votre document, vous devez insérer un chunk de code R pour préciser la localisation de votre environnement virtuel Python - comme nous utilisons knitr, c’est ce qui va permettre à reticulate de savoir où chercher les packages Python qui ont été installés pour cet atelier :
Comment connaitre la taille d’une cellule raster ?
En utilisant les paramètres décrivant la matrice de transformation de géoréférencement du jeu de données raster en question (cette transformation fait correspondre les coordonnées ligne/colonne des pixels aux coordonnées du système de référence de l’ensemble de données) :
transform = xds.rio.transform()
Puis en utilisant la première et la cinquième valeur qui représentent respectivement la largeur et la hauteur du pixel :
area_pixel = transform[0] *abs(transform[4]) # la hauteur est généralement négative
Comment faire pour qu’un chunk retourne du HTML ?
Par exemple en stockant du Markdown dans une chaîne de caractère et en le transformant en HTML avec IPython.display.Markdown :
from IPython.display import Markdownrendu ='''## Résumé| Catégorie | Superficie ||---------|----------|| Aahah | 21.34 |'''Markdown(rendu)
Observable JavaScript
Qu’est-ce que l’ojs ?
L’Observable javascript {ojs} est un ensemble d’améliorations apportées à vanilla JavaScript créé par Mike Bostock (également auteur de D3 (Bostock et al., 2011)). Observable JS se distingue par son exécution réactive, qui convient particulièrement bien à l’exploration et à l’analyse interactives des données.
Observable est aussi une startup fondée par Mike Bostock et Melody Meckfessel, qui propose une plateforme 100% en ligne (https://observablehq.com) pour concevoir, partager et diffuser des visualisations de données.
Observable c’est donc aussi C’est aussi une plateforme web qui héberge des notebooks computationnels sur la visualisation de données.
Objectifs
👉 Construire un langage et un écosystème dédié à la visualisation de données sur le web
👉 Fédérer et faire collaborer une communauté
Observable dans Quarto
Le runtime d’Observable est Open Source et est utilisé dans Quarto.
Dans Quarto, toutes les instructions s’écrivent dans des chunks {ojs}
```{ojs}```
On peut paramétrer l’affiche et/ou l’execution du code avec //| echo et //| eval.
```{ojs}//| echo: false//| eval: true```
NB : Contrairement aux chunks R et Python, le code s’execute just in time dans le navigateur web une fois le notebook généré. Seul l’export html est donc pris en compte.
Principes
Le langage JavaScript n’a pas du été conçu pour l’analyse de données. Mais, gros point fort, on a tous un navigateur web et JavaScript tourne sur toutes les machines. Le but : bâtir une évolution du langage JavaScript pour en faire un langage adapté à l’analyse et la visualisation de données sur le web.
l’ojs ressemble donc à du JavaScript, mais il y a quelques différences.
👉 Des bibliothèques préchargées 📊
OJS c’est du JavaScript + plein de bibliothèques pré chargées :
Symbol
Name
Version
_
Lodash
4.17.21
aq
Arquero
Arrow
Apache Arrow
4.0.1
d3
D3.js
dot
Graphviz
0.2.1
htl
Hypertext Literal
Inputs
Observable Inputs
L
Leaflet
mermaid
Mermaid
9.1.6
Plot
Observable Plot
SQLite
SQL.js
1.7.0
topojson
TopoJSON Client
3.1.0
vl
Vega, Vega-Lite
5.22.1, 5.2.0
👉 Des cellules 🙋
Chaque début de ligne identifie une cellule ojs. Chaque ligne définit une variable et une cellule qui doit être unique sur l’ensemble du document.
En début de cellule, on n’utilisera pas les déclarations de variables avec let, const ou var.
```{ojs}a =10// une celluleb =20// une cellule```
```{ojs}c = a + b // une cellule```
A l’intérieur d’un bloc de code, on écrit du vanilla JavaScript.
```{ojs}d = { let a =10let b =30return a + b } // Une cellule```
👉 Des promesses sont résolues ✅
Dans ojs, les promesses sont résolues d’une cellule à l’autre. Cela permet d’enlever de la complexité liée à la conception asynchrone du JavaScript. Ca va être très pratique quand on charge un jeu de données par exemple.
👉 L’ordre des cellules n’a pas d’importance 🤯
On peut donc écrire ça
```{ojs}result = val1 * val2```
avant ça
```{ojs}val1 =10val2 =20```
Le lien entre les cellules se fait de façon topologique et non de haut en bas.
👉 Tout est réactif 🔥
Dès lors qu’une cellule est modifiée, les cellules qui en dépendent sont modifiées également.
Pour modifier la valeur d’une cellule, on utilise des Inputs (des composants proposés par OJS pour explorer les données) avec le mot clé viewof
viewof x = Inputs.range([0,255], {step:1})
viewof y = Inputs.text({value:30})
viewof z = Inputs.radio([10,100,1000], {value:10})
x * y * z
Ce système est pratique pour modifier des visualisations de données.
Ici, un exemple avec un simple cercle dessiné en svg.
viewof color = Inputs.color({ label:"Couleur",value:"#4ab58c" })viewof radius = Inputs.range([1,50], { label:"Rayon",step:1,value:100 })viewof position = Inputs.range([50,950], { label:"Position",step:1,value:50 })
L’écosystème spatial de JavaScript est moins étoffé que ce qu’on peut trouver en Python ou en R. Mais on trouve quand même des choses intéressantes qui permettent de faire de la géomatique directement dans le navigateur web. Voici quelques exemples.
Les fonctionnalités spatiales de d3.js
d3-geo est le module spatial de D3. Il permet de gérer les projections cartographiques, les formes sphériques et la trigonométrie sphérique. Voir aussi d3-geo-projection. d3-delaunay est une bibliothèque rapide pour calculer le diagramme de Voronoï d’un ensemble de points bidimensionnels. Elle est basée sur Delaunator, une bibliothèque rapide pour calculer la triangulation de Delaunay en utilisant des algorithmes de balayage. Le diagramme de Voronoï est construit en reliant les circonférences des triangles adjacents dans la triangulation de Delaunay. d3-geo-voronoi est une adaptation de d3-delaunay sur la sphère.
TopoJSON est une extension de GeoJSON. La librairie permet de réaliser des opérations SIG basées sur la topologie (agrégation, voisins, simplification, etc.)
Les bibliothèques SIG
turf est une bibliothèque JavaScript pour l’analyse spatiale (de type SIG). Elle comprend les opérations spatiales traditionnelles, des fonctions d’aide pour la création de données GeoJSON, ainsi que des outils de classification des données et de statistiques. L’avantage de turf est d’être modulaire. On ne charge que ce dont on a besoin.
spl.js est une implémentation de SpatiaLite (et de ses amis : sqlite, geos, proj, rttopo) pour le navigateur.
geotoolbox est un outil javascript pour les géographes basé sur d3geo, topojson et jsts. Il permet de traiter simplement les propriétés geojson et fournit plusieurs opérations SIG utiles pour la cartographie thématique.
Gdals3.js est un portage des applications Gdal (gdal_translate, ogr2ogr, gdal_rasterize, gdalwarp, gdaltransform) vers Webassembly. Il permet de convertir des données géospatiales matricielles et vectorielles dans divers formats et systèmes de coordonnées.
proj4js est une bibliothèque JavaScript permettant de transformer les coordonnées d’un système de coordonnées à un autre, y compris les transformations de datum
Dataviz / cartographie
plot est la bibliothèque de visualisation de données développée par Observable (Mike Bostock et Philippe Rivière). Au départ pensée pour la visualisation de données statistiques, elle permet aujourd’hui de réaliser des cartes. Plot est disponible par défaut dans les cellules {ojs}.
vega-lite est un outil de visualisation mettant en œuvre une grammaire graphique, similaire à ggplot2. Cette bibliothèque est disponible par défaut dans les cellules {ojs}.
bertin est une bibliothèque écrite en JavaScript qui permet de réaliser des cartes thématiques pour le web. Sa conception vise à permettre aux utilisateurs de créer rapidement des cartes thématiques interactives sans forcement connaître le langage JavaScript.
Gridviz est une bibliothèque JavaScript basée sur WebGL développée par Julien Gaffuri et Joe Davies. Elle permet de visualiser des données carroyées (ou tout ensemble de données tabulaires avec une position x/y) dans le navigateur dans une grande variété de styles cartographiques.
go-cart-wasm est une bibliothèque JS/WASM permettant de réaliser des cartogrammes basés sur le flux (comme décrit dans “Gastner, Seguy, and More (2018)).
Importer des données
Pour importer des données en {ojs}, on utilise l’instruction FileAttachment()
On peut aussi récupérer des données depuis R ou Python avec l’instruction ojs_define
NB
Souvenez vous que le code JavaScript s’execute dans le navigateur web. Il s’execute donc après le code R et Python.
ojs_define fonctionne bien pour des simples variables ou des data frames. Mais ne renvoie pas un geoJSON correctement formaté à partir d’un spatial dataframe.
chunck r
ojs_define(test = mun)
Chuck ojs
test
Une solution consiste à utiliser le package geojsonsf.
bertin.draw({params: { extent: [[-79,44.5], [-66,51]]},layers: [ { type:"header",text:"Population en 2022",anchor:"start" },// Titre { type:"minimap",x:10,y:10,width:160 }, {id:"bub",// identifiant de la couchetype:"bubble",// couche de symboles proportionnelsgeojson: munic,values:"POP_2022",// variable à cartographier k:50,// Rayon du plus gros cercleleg_x:740,leg_y:350,leg_round:0,leg_title:"Nombre d'habitants",fill:"red",fillOpacity:0.5,tooltip: ["$MUS_NM_MUN", d => d.properties["POP_2022"] +" habitants"] // infobulle }, {geojson: munic,fill:"#CCC"},// Couche des communes {type:"scalebar"} ]})
On peut facilement combiner cette couche de cercles avec un une carte choroplèthe.
bertin.draw({params: { extent: [[-79,44.5], [-66,51]]},layers: [ { type:"header",text:"Population en 2022",anchor:"start" },// Titre { type:"minimap",x:10,y:10,width:160 }, {id:"bub",type:"bubble",geojson: munic,values:"POP_2022",k:50,leg_x:690,leg_y:370,leg_round:0,leg_title:"Nombre d'habitants",fill:"white",fillOpacity:0.2,stroke:"black",strokeWidth:1.4,tooltip: ["$MUS_NM_MUN", d => d.properties["POP_2022"] +" habitants"] }, { geojson: munic,fill: {type:"choro",values:"VIT_ECO",colors:"RdYlBu",leg_x:870,leg_y:315,leg_title:"Indice de\nvitalité\néconomique",leg_round:2 } },// Couche des communes {type:"scalebar"} ]})
On peut aussi colorier les cercles
bertin.draw({params: { extent: [[-79,44.5], [-66,51]]},layers: [ { type:"header",text:"Population en 2022",anchor:"start" },// Titre { type:"minimap",x:10,y:10,width:160 }, {id:"bub",type:"bubble",geojson: munic,values:"POP_2022",k:50,leg_x:690,leg_y:370,leg_round:0,leg_title:"Nombre d'habitants",fill: {type:"choro",values:"VIT_ECO",colors:"RdYlBu",leg_x:870,leg_y:315,leg_title:"Indice de\nvitalité\néconomique",leg_round:2 },tooltip: ["$MUS_NM_MUN", d => d.properties["POP_2022"] +" habitants"] }, { geojson: munic,fill:"#CCC" },// Couche des communes {type:"scalebar"} ]})
On peut utiliser les Inputs pour tester des visualisations, explorer les données ou proposer des interactions à un utilisateur.
Bivand, R. (2021). Progress in the R ecosystem for representing and handling spatial data. Journal of Geographical Systems, 23(4), 515‑546. https://doi.org/10.1007/s10109-020-00336-0
Bostock, M., Ogievetsky, V. et Heer, J. (2011). D³ Data-Driven Documents. IEEE Transactions on Visualization and Computer Graphics, 17(12), 2301‑2309. https://doi.org/10.1109/TVCG.2011.185
Dorman, M., Graser, A., Nowosad, J. et Lovelace, R. (2022). Geocomputation with Python. CRC Press. https://py.geocompx.org/
Gillies, S., Buffat, R., Arnott, J., Taves, M. W., Wurster, K., Snow, A. D., Cochran, M., Sales de Andrade, E. et Perry, M. (2023, janvier). Fiona. https://github.com/Toblerity/Fiona
Gillies, S., Wel, C. van der, Van den Bossche, J., Taves, M. W., Arnott, J., Ward, B. C., et al. (2023, janvier). Shapely. https://doi.org/10.5281/zenodo.5597138
Giraud, T. et Lambert, N. (2017). Reproducible Cartography. In M. Peterson (dir.), Advances in Cartography and GIScience. ICACI 2017. Lecture Notes in Geoinformation and Cartography. (p. 173‑183). Springer. https://doi.org/10.1007/978-3-319-57336-6_13
Hoyer, S., Roos, M., Joseph, H., Magin, J., Cherian, D., Fitzgerald, C., Hauser, M., Fujii, K., Maussion, F., Imperiale, G., Clark, S., Kleeman, A., Nicholas, T., Kluyver, T., Westling, J., Munroe, J., Amici, A., Barghini, A., Banihirwe, A., Bell, R., et al.Wolfram, P. J. (s. d.). xarray. https://doi.org/10.5281/zenodo.598201
Jordahl, K., Bossche, J. V. den, Fleischmann, M., Wasserman, J., McBride, J., Gerard, J., Tratner, J., Perry, M., Badaracco, A. G., Farmer, C., Hjelle, G. A., Snow, A. D., Cochran, M., Gillies, S., Culbertson, L., Bartos, M., Eubank, N., maxalbert, Bilogur, A., Rey, S., et al.Leblanc, F. (2020, juillet). geopandas/geopandas: v0.8.1. Zenodo. https://doi.org/10.5281/zenodo.3946761
Lambert, N., Giraud, T., Viry, M. et Ysebaert, R. (2023). Cartographier pour le web avec la bibliothèque JavaScript « bertin ». SAGEO.
Lovelace, R., Nowosad, J. et Muenchow, J. (2019). Geocomputation with R. CRC Press. https://r.geocompx.org/
Padgham, M., Rudis, B., Lovelace, R. et Salmon, M. (2017). osmdata. The Journal of Open Source Software, 2(14). https://doi.org/10.21105/joss.00305
Pebesma, E. (2018). Simple Features for R: Standardized Support for Spatial Vector Data. The R Journal, 10(1), 439‑446. https://doi.org/10.32614/RJ-2018-009
R Core Team. (2023). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. https://www.R-project.org/
Rey, S. J. et Anselin, L. (2007). PySAL: A Python Library of Spatial Analytical Methods. The Review of Regional Studies, 37(1), 5‑27.
Snow, A. D., Brochart, D., Raspaud, M., Taves, M., Bell, R., RichardScottOZ, Chegini, T., stefank0, Amici, A., Braun, R. et al., et. (2023). corteva/rioxarray: 0.14.1 Release. https://doi.org/10.5281/zenodo.7829704
Snow, A. D., Whitaker, J., Cochran, M., Miara, I., Bossche, J. V. den, Mayo, C., Cochrane, P., Lucas, G., Kloe, J. de, Karney, C. et al., et. (2023). pyproj4/pyproj: 3.5.0 Release. https://doi.org/10.5281/zenodo.7776548
Wickham, H. (2016). ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org
Wickham, H., Averick, M., Bryan, J., Chang, W., McGowan, L. D., François, R., Grolemund, G., Hayes, A., Henry, L., Hester, J., Kuhn, M., Pedersen, T. L., Miller, E., Bache, S. M., Müller, K., Ooms, J., Robinson, D., Seidel, D. P., Spinu, V., Takahashi, K., et al.Yutani, H. (2019). Welcome to the tidyverse. Journal of Open Source Software, 4(43), 1686. https://doi.org/10.21105/joss.01686