Dans ce billet, je vais consolider les différents fichiers au format GPX des travaux routiers publiés par Transports Québec en un seul fichier GeoJSON qui peut être utilisé dans n'importe quel logiciel de système d'information géographique (GIS).

Téléchargement des données

Flux de traitement des données

Je vais d'abord télécharger les données en utilisant le logiciel wget en ligne de commande bash et une boucle pour chacune des régions ayant des fichiers disponibles.

#!/bin/bash
for i in $(echo "for (i=1;i<=13;i++) i*1000"| bc)
do
wget "https://www.quebec511.info/fr/Diffusion/EtatReseau/TravauxGps.aspx?idreg=${i}&p=0&action=gps" \
--output-document="travaux_gps_${i}.gpx"
done

Un des principaux avantages d'avoir codé cette opération de téléchargement sous la forme d'un script en ligne de commande bash est que je peux l'exécuter à volonté pour automatiquement mettre à jour la liste de fichiers. De plus, il serait possible de l'exécuter périodiquement à l'aide d'un outil d'ordonnancement tel que cron.

Préparation de l'environnement de travail en R

Dans R, je charge d'abord les packages requis pour le traitement qui suit.

library(sf)
library(dplyr)
library(stringr)
library(lubridate)
library(ggplot2)
library(ggmap)

Je liste les fichiers GPX contenus dans le répertoire de travail:

gpxfiles <- list.files("~/Nextcloud/gpx/",
                       pattern = "*.gpx")
##  [1] "travaux_gps_1000.gpx"  "travaux_gps_10000.gpx" "travaux_gps_11000.gpx"
##  [4] "travaux_gps_12000.gpx" "travaux_gps_13000.gpx" "travaux_gps_2000.gpx" 
##  [7] "travaux_gps_3000.gpx"  "travaux_gps_4000.gpx"  "travaux_gps_5000.gpx" 
## [10] "travaux_gps_6000.gpx"  "travaux_gps_7000.gpx"  "travaux_gps_8000.gpx" 
## [13] "travaux_gps_9000.gpx"

Extraire les informations depuis les métadonnées avec une fonction

Comme les fichiers GPX ont une capacité limitée à contenir une variété d'attributs associés aux entités géospatiales, les informations telles que les dates de début et de fin des travaux ou le numéro de la route concernée sont concaténées dans le champs name et desc.

Je crée ici une fonction qui permet de lire un fichier GPX et d'extraire ces champs ainsi que la géométrie associée, et retourner le tout sous la forme d'un data.frame, qui est la structure par défaut de traitement des données en R.

getwaypoint <- function(gpxfile)
{
  gpxfile %>%
    st_read(layer = "waypoints", quiet=TRUE) %>%
    select(name, desc, geometry) %>%
    as.data.frame()
}

La fonction lapply permet d'appliquer une fonction sur chacun des éléments d'une liste et de retourner une liste de même longueur. C'est l'équivalent de la list comprehension de Python.

Traitement des données

J'applique ici la fonction à la liste des chemins des fichiers créée précédemment.

J'ajoute aussi une clé synthétique à chaque rangée (rowkey) afin de ne pas avoir à effectuer de fusion de tables sur des champs texte complexes (conseil d'ami, ne fusionnez jamais des données sur un champ en texte libre).

J'ajoute enfin la date du jour dans une colonne, afin d'identifier la journée où les données ont été téléchargées, comme cette information n'apparaît pas dans le fichier.

mywaypoints <- lapply(gpxfiles, getwaypoint) %>%
  bind_rows() %>%
  mutate(rowkey = seq_along(name),
         record_date = today())

J'utilise ici une expression régulière afin d'extraire le type de travaux, la date de début et la date de fin depuis le champ name.

name_groups <- mywaypoints$name %>%
  str_match(pattern = "^([A-Za-z]+)\\s+([0-9]{1,2}/[0-9]{1,2}/[0-9]{1,2})\\s+([0-9]{1,2}/[0-9]{1,2}/[0-9]{1,2})$") %>%
  `colnames<-`(c("name", "type", "date_debut", "date_fin")) %>%
  as.data.frame %>%
  mutate(rowkey = mywaypoints$rowkey)

J'effectue un traitement similaire pour le champ desc qui contient le nom de la route, une date de début et de fin associée à la route ainsi que d'autres informations superflues.

desc_groups <- mywaypoints$desc %>%
  str_match(pattern = "^(.*)\\s+([0-9]{1,2}/[0-9]{1,2}/[0-9]{1,2})\\s+([0-9]{1,2}/[0-9]{1,2}/[0-9]{1,2})\\s+") %>%
  `colnames<-`(c("desc", "route", "date_debut_rte", "date_fin_rte")) %>%
  as.data.frame %>%
  mutate(rowkey = mywaypoints$rowkey)

Fusion des attributs

Je fusionne ici les données extraites aux données originales en utilisant la clé synthétique (rowkey) que j'ai générée plus tôt. Je conserve seulement les champs qui ont des informations pertinentes à l'aide d'un select.

mywaypoints2 <- mywaypoints %>%
  inner_join(name_groups, by = "rowkey") %>%
  inner_join(desc_groups, by = "rowkey") %>%
  select(
    record_date,
    type,
    date_debut,
    date_fin,
    route,
    date_debut_rte,
    date_fin_rte,
    geometry
  )

Exporter notre fichier GeoJSON

Pourquoi le GeoJSON ?

Le GeoJSON est un format de données ouvert (il n'appartient pas à une entreprise) et il est très polyvalent. Il permet de stocker des données géospatiales ainsi que des tables d'attributs, est lisible par l'humain (c'est du texte et non du langage binaire) et est facile à importer dans tous les systèmes d'information géographiques, des plus simples comme Leaflet aux plus complexes comme ArcGIS.

Comme c'est aussi un fichier JSON (le format de données du langage javascript, omniprésent sur le web), il peut être lu et interprété par tous les navigateurs web modernes.

Allons-y

Je me prépare ici à exporter les données. Comme la fonction utilisée ci-dessous ne permet pas d'écraser un fichier GeoJSON, je supprime ici le fichier s'il existe.

export_geojson_path <- "~/Nextcloud/gpx/all.geojson"
succes.supprimer <- if (file.exists(export_geojson_path)) {
  file.remove(export_geojson_path)
}

J'utilise la fonction st_write pour écrire un fichier GeoJSON aves les données finales.

Visualisation des données

Enfin, avant de conclure ce billet, profitons-en pour visualiser les données à des fins de validation de qualité.

Note: Ce n'est pas ici que l'on sort nos talents de graphiste, on valide seulement si les données font du sens sur la carte !

mywaypoints2_df <- fortify(mywaypoints2)

Je calcule d'abord l'étendue des données pour télécharger le fond de carte correspondant.

mywaypoints2_bbox <- st_bbox(mywaypoints2_df$geometry) %>% as.list()

Je télécharge ensuite le fond de carte.

mapImage <- get_map(
  c(
    left = mywaypoints2_bbox$xmin,
    bottom = mywaypoints2_bbox$ymin,
    right = mywaypoints2_bbox$xmax,
    top = mywaypoints2_bbox$ymax
  ),
  maptype = "terrain",
  zoom = 7,
  source = "stamen"
)

Enfin, j'affiche les points sur une carte

plot(st_transform(mywaypoints2_df$geometry, crs = 3857), 
     bgMap = mapImage, 
     pch = 3,
     main="Travaux routiers au Québec",
     axes = TRUE
     )

Carte des travaux routiers au Québec

Conclusion

Dans ce billet, j'ai présenté une méthode avec le langage R pour rassembler des fichiers de points GPX en un seul fichier GeoJSON enrichi d'informations telles que les dates de début et de fin des travaux routiers. J'ai ensuite effectué une validation de données à l'aide d'une visualisation simple.

Je vous souhaite bonne route et évitez les chantiers routiers si possible à l'aide de ces données !


Tu aimerais apprendre à découvrir et maîtriser les données autour de toi pour créer des outils pratiques qui te simplifient la vie ?

Je lance ma newsletter mensuelle avec une tonne d'astuces sur la programmation en ligne de commandes, l'analyse de données et l'autonomie en informatique.

Article précédent