Analyse d’accessibilité multimodale

Avec R et OpenTripPlanner

Jean-Clément Ullès (LAGAM, Université Paul Valéry Montpellier 3)

Marion Le Texier (LAGAM, Université Paul Valéry Montpellier 3)

featured

Cette fiche présente une méthodologie d’analyse de l’accessibilité multimodale d’un territoire à partir des données ouvertes d’offre de transport et de mobilité et du logiciel OpenTripPlanner mobilisé depuis l’environnement R.

1 Introduction

L’analyse de l’accessibilité multimodale est au coeur des politiques de transport, et constitue l’un des enjeux majeurs de la planification territoriale. L’émergence de nouvelles données de suivi de l’offre de transport et d’outils adaptés à leur analyse permet aujourd’hui de porter un regard fin sur le domaine. C’est le cas de l’outil OpenTripPlanner qui fait l’objet de ce tutoriel.

Celui-ci a été construit dans le but d’accompagner son apprentissage via l’usage du langage R, et se distingue des documentations existantes par l’entrée thématique qu’il propose. Il repose sur un cas d’étude illustratif des potentialités d’analyse offertes par le croisement d’OTP et des nouvelles données ouvertes décrivant l’offre de transport : la métropole de Montpellier (Montpellier Méditerranée Métropole ou Montpellier 3M).

1.1 Accessibilité multimodale

L’accessibilité est un concept utilisé en géographie pour qualifier “la plus ou moins grande facilité avec laquelle [un] lieu peut être atteint à partir d’un ou de plusieurs autres lieux, par un ou plusieurs individus susceptibles de se déplacer à l’aide de tout ou partie des moyens de transport existants” (Laurent Chapelon in Encyclopédie Hypergeo).

Étudier l’accessibilité multimodale d’un lieu ou d’un ensemble de lieux revient donc à mesurer puis à comparer les possibilités de déplacement et leur pénibilité associée en tenant compte de l’ensemble des modes : transports en commun, marche, vélo, voiture, notamment.

1.2 Le projet OpenTripPlanner

OpenTripPlanner est un logiciel libre développé en Java permettant de créer et d’interroger un planificateur d’itinéraire multimodal personnalisé. Créé en 2009 pour TriMet, l’agence de transport en commun de Portland (Oregon), par l’équipe d’informaticiens du consultant Conveyal, OTP s’est ensuite développé, et englobe aujourd’hui une communauté mondiale d’utilisateurs et de développeurs.

L’analyse de l’accessibilité par des réseaux de transport multimodaux, opérant à différentes échelles et gérés par des autorités organisatrices variées, marque une étape importante dans le champ de la géographie des transports. Les déplacements optimaux déterminés par OTP correspondent au plus court chemin, c’est-à-dire à la minimisation du temps de déplacement pour l’usager? Ces temps sont calculés en utilisant les algorithmes de Dijkstra (Dijkstra et al. 2021), A* (Hart et al. 1968) et assimilés. L’intermodalité (combinaison de plusieurs modes et réseaux de transport) et l’intramodalité (combinaison de plusieurs véhicules au sein d’un même réseau de transport) sont également intégrées dans OTP, avec la gestion optimale des correspondances (minimisation du temps de marche entre deux arrêts et du temps d’attente). OTP se prête donc aux analyses territoriales de l’accessibilité, en simulant les meilleurs déplacements possibles pour l’usager en fonction des différents modes de transport à sa disposition pour un horaire donné.

Brièvement, OTP construit un graphe décrivant les itinéraires reliant l’ensemble des localisations d’un territoire donné, en transport en commun comme par la route, à partir de données GTFS (General Transit Feed Specification) pour le réseau de transport en commun (bus, métros, tramways, trains) et de données OpenStreetMap pour les modes de transport individuels qui utilisent le réseau viaire (voiture, vélo, piéton). Le logiciel dépend donc de données ouvertes renseignées dans des formats de fichier standards. Sur ce point, rappelons que les autorités organisatrices de la mobilité (AOM) ont l’obligation de transmettre leurs données de l’offre de transport (règlement européen 2017/1926, art. 25 de la LOM de 2019) sur le point d’accès national (PAN).

Le code source d’OTP est publié en accès libre sur la plateforme GitHub.

Notons l’existence de la bibliothèque r5r qui utilise le logiciel Java open source R5, dérivé d’OpenTripPlanner. Il a été mis en place par l’équipe de consultant informatique Conveyal et adapté à R par Pereira et al. (2021). Il propose des fonctionnalités analogues à la bibliothèque opentripplanner (calcul d’accessibilité, matrice O/D, isochrones, etc.).

1.3 Ressources

Les principales ressources recommandées en complément de ce tutoriel sont disponibles aux liens suivants :

2 Les données de l’offre de transport

2.1 Les données GTFS

2.1.1 Présentation générale

L’offre de transport théorique dans un territoire peut être étudiée à partir de données croisant les itinéraires (organisation des parcours et localisation des arrêts) et les horaires de passage des véhicules.

Deux formats de données statiques sont privilégiés :

  • NeTEx (Network Timetable Exchange), répondant aux critères établis par le Comité européen de normalisation en électronique et en électrotechnique

  • GTFS (General Transit Feed Specification), répondant aux critères spécifiques des flux Google Transports en commun public. Les données doivent être validées par le validateur GTFS de Google.

2.1.2 Exemple des données de Montpellier 3M

Nous utiliserons dans ce tutoriel le cas de la métropole de Montpellier. La métropole est constituée de 31 communes comptant 499 761 habitants en 2020 (INSEE RP2020). Elle détient la compétence d’autorité organisatrice des mobilités (AOM) sur son ressort territorial et opère 4 lignes de tramway et 41 lignes de bus. Cet exemple servira d’illustration des méthodes présentées dans cette fiche afin de disposer d’un cas d’étude simplifié, mais d’autres échelles territoriales sont bien sûr possibles en ajoutant les données de l’offre en adéquation avec l’échelle d’analyse (offre régionale, ferroviaire, autres AOM locales, etc.). Les données renseignant l’offre de transport théorique de TAM (Transports de l’agglomération de Montpellier) sont fournies au format GTFS depuis le point d’accès national aux données de transport, et peuvent être converties au format GeoJSON et au format NeTEx directement depuis le site de téléchargement.

L’extraction des données utilisées pour ce tutoriel a été réalisée le 27 mars 2023. Elles décrivent l’offre de transport de façon statique pour la période s’écoulant du 16 mars 2023 au 14 mai 2023. Les données utilisées pour ce tutoriel ont été actualisées pour la dernière fois à la date du 25 mars 2023.

Le dossier contient différents fichiers au format texte, renseignant chacun une partie du système de transport sous la forme d’un tableau de données utilisant des virgules comme séparateurs de colonnes :

  • agency : le fichier contient des informations sur les compagnies (agences) de transport en commun opérant sur le réseau étudié (ex : identifiant, nom, site Web, téléphone, fuseau horaire…)

  • routes : le fichier catalogue les lignes du réseau de transport, qu’il décrit par le biais d’identifiants et de noms courts ou longs, par type de transport, par mode de ramassage et de descente, et d’autres informations plus ou moins formalisées. Une couleur peut être associée à une route afin d’en faciliter le repérage par les utilisateurs

  • trips : le fichier décrit les itinéraires réalisés sur les routes (lignes), notamment par le biais d’informations sur les sens de déplacement et les dates de service (blocks). Plusieurs trips peuvent donc avoir lieu sur une même route

  • stops : le fichier identifie, nomme, géolocalise (coordonnées lat/long) et caractérise (ex : type d’arrêt, identifiant de l’emplacement parent, accessibilité aux personnes en fauteuil roulant…) les arrêts du réseau

  • stop_times : le fichier renseigne, pour chaque itinéraire (trip) concerné, les heures de passage (arrivée et départ) à l’arrêt. Des informations sur les modes de rammassage/descente, l’affichage sur les panneaux et d’autres caractéristiques des arrêts sont proposées. L’ordonnancement des passages aux arrêts pour un itinéraire particulier est également consigné

  • transfers : le fichier est optionnel. Il permet de spécifier les règles de transit entre deux arrêts, en termes d’éloignement géographique et de durée de déplacement à pied, ou, comme avec les données de la TAM, sous la forme d’une variable catégorielle qui renseigne sur la possiblité de réaliser un transfert entre deux arrêts (transfert recommandé, programmé, possible sous contrainte de temps ou impossible)

  • calendar : le fichier recense les dates pour lesquelles un service est fonctionnel : une date de début et une date de fin indiquent la plage de dates d’activité du service, et, pour chaque jour de la semaine (lundi à dimanche), une variable renseigne sur la disponibilité (ou l’indisponibilité) du service pour l’ensemble des lundis (resp. mardis, mercredis, jeudis, vendredis, samedis et dimanches) de la plage

  • calendar_dates : le fichier renseigne sur les exceptions au service régulier consigné dans le fichier calendar. Il peut par exemple servir à indiquer les changements d’activité du service (ajout ou suppression) du fait d’un événement particulier, ou pour s’adapter à un calendrier scolaire.

De nombreuses dépendances existent donc entre les différents fichiers constituant la base de données GTFS, et ces fichiers peuvent être plus ou moins nombreux comme l’illustre le diagramme de classe proposé sur la page Wikipédia associée et reproduite ci-dessous.

2.2 Les données OpenStreetMap

L’utilisation du réseau viaire (routes, chemins) est indispensable pour calculer les itinéraires des modes individuels (voiture, piéton et cycliste) et des modes collectifs (pré et post-acheminement, gestion des correspondances). Pour cela, nous utiliserons les données issues du projet de cartographie collaborative OpenStreetMap (OSM).

La complétude et la précision des données varient d’un territoire, d’une échelle géographique et d’une temporalité à l’autre (Herfort et al. 2022). Il faut donc être vigilant à cet aspect en amont de leur utilisation dans le cadre d’une analyse d’accessibilité qui utilise le réseau viaire. En France, la plateforme recense l’ensemble des données géographiques publiques en open data en complément de l’information géographique volontaire. Une présentation des données OSM est par exemple proposée par Mericskay (2017).

2.2.1 Téléchargement des données depuis la Geofabrik

La société allemande Geofabrik GmbH propose au téléchargement des instantanés de la base de données OSM pour un espace géographique donné. L’échelle géographique la plus fine correspond aux anciennes régions françaises. Les données décrivant la métropole de Montpellier Méditerranée sont donc contenues dans le paquet décrivant le Languedoc-Roussillon.

La bibliothèque osmextract permet de télécharger et d’importer facilement des données OpenStreetMap de Geofabrik et d’autres fournisseurs depuis R.

library(osmextract)

Nous allons utiliser le fichier décrivant les limites de la métropole de Montpellier Méditerranée afin de créer un filtre spatial lors du téléchargement des données OSM depuis la Geofabrik. Pour cela, nous l’importons dans le projet en cours via la fonction st_read() de la bibliothèque sf.

Le fichier GeoJSON décrivant le territoire de la métropole de Montpellier est disponible ici :


Télécharger les données

library(sf)
epci_3m <- st_read("data/EPCI_MONTPELLIER.geojson")

La fonction oe_get() de la bibliothèque osmextract permet d’associer par le biais d’une relation d’appartenance géographique un lieu d’entrée (option place) avec l’URL du fichier .osm.pbf correspondant (dans le cas de la métropole de Montpellier, il s’agit de l’ancienne région Languedoc-Roussillon), qu’elle enregistre dans une liste puis télécharge. Le paramètre boundary permet d’associer un filtre spatial à la requête, et de ne télécharger que les objets contenus dans les limites géographiques désignées (et renseignées au format sf/sfc/bbox). Notons que la fonction appelle en interne la fonction oe_vectortranslate() qui converti le fichier .osm.pbf en fichier geopackage (.gpkg) avant de le lire. Le filtre spatial ne s’applique que sur le fichier .gpkg qu’elle télécharge, et pas sur le fichier .osm.pbf étant lui aussi téléchargé. Des options d’enregistrement du fichier peuvent être ajoutées, par exemple afin de forcer la mise à jour si le fichier a été téléchargé auparavant (paramètre force_download) ou pour indiquer le chemin d’accès du répertoire dans lequel enregistrer le fichier (paramètre download_directory) en remplacement de la valeur par défaut (répertoire temporaire).

contours_3m <- st_as_sf(st_union(epci_3m))

osm_3m <- oe_get(place = contours_3m, 
                      boundary = contours_3m, 
                      force_download=TRUE, 
                      download_directory=getwd())

Notons qu’il est possible d’extraire les données OSM au format .pbf depuis une source externe, comme le propose le site BBBike extract par exemple. Le site permet notamment de définir un cadre de délimitation (bounding box) pour les informations spatiales à télécharger, ce qui réduira a posteriori la taille du graphe construit pour lancer les requêtes OTP.

2.2.2 Description des données .osm.pbf

Le format des données ainsi téléchargées (Protocolbuffer Binary Format) est spécifique au projet OpenStreetMap, mais utilise le format de sérialisation de Google pour produire un fichier au format binaire. Il permet de regrouper des informations multiples (entités, objets, tuiles) de façon indépendante.

Par curiosité (mais cela n’est pas une étape nécessaire pour la mise en oeuvre de la chaîne de traitements), on peut importer les couches d’information géographique souhaitées une à une ou de façon groupée via la fonction st_read(), en spécifiant la couche (organisée par type de géométrie) et en créant une requête sur les attributs, par exemple en sélectionnant les routes (key = highway) dans la couche.

osm_highways <- sf::read_sf("geofabrik_languedoc-roussillon-latest.osm.pbf", 
                            query = "select highway from lines")

2.2.3 Utilisation des tags OSM pour les itinéraires routiers

OTP permet de définir un ensemble de critères pour décider quand une rue est autorisée aux piétons, aux vélos ou aux voitures. Par défaut, OTP utilise les tags OSM et considère que :

  • les mobilités piétonnes et cyclables ne sont pas autorisées sur les routes décrites comme highway=trunk, highway=trunk_link, highway=motorway, highway=motorway_link, ou highway=construction

  • les mobilités piétonnes et cyclables sont toutes les deux autorisées sur les routes décrites comme highway=pedestrian, highway=cycleway, et highway=footway

  • les vélos ne sont pas autorisés sur les routes de type highway=footway lorsque lui sont associées les étiquettes suivantes : footway=sidewalk, public_transport=platform, ou railway=platform.

Depuis la version 2 d’OTP, le logiciel permet de définir des niveaux de sécurisation des routes pour les trajets piétons et cyclables en fonction des tags OSM. Ces options ne sont pas intégrées dans la version 1.5 du logiciel mobilisée dans la librairie R faisant l’objet de cette fiche.

2.3 Relations entre données GTFS et données OpenStreetMap

Si un arrêt de transport, localisé par le GTFS, se trouve dans un bâtiment, il est nécessaire d’être vigilant à sa connexion au graphe OpenStreetMap, sans quoi l’isolement de l’arrêt rendra impossible les parcours à pied (impossibilité de créer les pré- et post-acheminements ainsi que les correspondances à pied entre deux arrêts).

Par exemple, les cheminements piétons dans les gares ne sont pas systématiquement renseignés dans OpenStreetMap, ni les escaliers ou ascenseurs permettant de changer de quai. D’expérience, les gares les plus importantes sont correctement géoréférencées mais toutes ne le sont pas. Un arrêt isolé du graphe piéton ne permet pas au logiciel de comprendre qu’il peut entrer et sortir de la gare, ou réaliser une correspondance entre un TGV et un TER dans la même gare s’il doit s’y déplacer, faute d’un graphe piéton disponible.

Un arrêt de transport renseigné peut aussi avoir des coordonnées géographiques erronées qu’il est indispensable de modifier.

Il nous semble que deux solutions peuvent alors être envisagées :

  • Contribuer à OpenStreetMap pour ajouter les informations manquantes. Les données .osm.pbf, parmi d’autres formats disponibles, de la Geofabrik sont quotidiennement mises à jour

  • Modifier la localisation du point d’arrêt créé par le GTFS. Cela peut être intéressant pour déplacer un arrêt avec des coordonnées imprécises ou erronées. Ainsi, le point peut être replacé dans un espace connecté au graphe piéton, comme l’entrée de la gare. Les coordonnées des arrêts sont rangées dans le fichier stops.

3 Préparation de l’environnement R pour OTP

3.1 La bibliothèque opentripplanner

OTP étant défini en Java, son fonctionnement nécessite l’entrée de lignes de commandes Java. La bibliothèque opentripplaner permet de définir les paramètres de ces fonctions depuis R et de les traduire automatiquement en Java.

La version de la bibliothèque R utilisée pour ce tutoriel est la 0.5.2, publiée en janvier 2023 et téléchargée depuis le CRAN le 21 mai 2024. Son exécution nécessitera l’installation préalable de Java 8. Attention, des conflits avec des versions postérieures ou ultérieures de Java sont à attendre, il est donc recommandé de désinstaller ces versions en amont de l’installation de Java 8 et du lancement d’OTP.

library(opentripplanner)

Cette partie du tutoriel est basée sur la documentation proposée par Marcus Young, Malcom Morgan et Robin Lovelace, les développeurs du package.

3.2 Installation d’OTP

Nous allons donc utiliser OTP en local, en téléchargeant la version 1.5 du logiciel depuis R (il s’agit de la version compatible avec la version 0.5 de la librairie R, d’autres versions d’OTP plus récentes ont vu le jour depuis).

3.2.1 Création du répertoire

OTP est distribué sous la forme d’un unique fichier .jar (Java ARchive) exécutable de façon autonome. Dans un premier temps, nous allons créer le répertoire dans lequel l’enregistrer.

La fonction file.path() permet de spécifier le chemin d’accès vers un fichier en concaténant des chaînes de caractères. Par défaut, le séparateur utilisé est “/”.

path_data <- file.path("data/OTP")

3.2.2 Pré-construction du fichier .jar

La fonction otp_dl_jar() de la bibliothèque opentripplanner télécharge le fichier JAR (Java Archive) d’OTP. Par défaut, le fichier JAR est caché dans un sous-répertoire de la bibliothèque. Si l’on souhaite spécifier la localisation du fichier .jar, il faut en indiquer le chemin d’accès (path =) et spécifier que l’on ne souhaite pas utiliser le cache par défaut (cache = FALSE).

download_otp <- otp_dl_jar(path = path_data, cache = FALSE)

Il faudra penser à commenter cette ligne de code une fois le téléchargement réalisé afin de ne pas le ré-exécuter à chaque relance du script.

Si le téléchargement du fichier via R ne fonctionne pas, vous pouvez télécharger manuellement le fichier depuis le dépôt maven.

On indique ensuite dans un vecteur au format “caractères” le chemin d’accès complet vers l’archive d’OTP :

path_otp <- paste(path_data, "/otp-1.5.0-shaded.jar", sep = "")

3.3 Préparation des fichiers d’entrée

En amont de la création du graphe sur lequel les calculs d’itinéraires seront réalisés par OTP, il est nécessaire de regrouper les fichiers d’entrée au sein du répertoire contenant le fichier JAR :

  • créer un sous-répertoire nommé graphs (/graphs)

  • à l’intérieur du sous-répertoire graphs, créer un nouveau sous-répertoire nommé default (/graphs/default)

  • dans le sous-répertoire default, déposer le fichier .osm.pbf du Languedoc-Roussillon et le fichier contenant les données GTFS au format zippé (gtfs.zip).

3.4 Création du graphe

Afin de construire les itinéraires, OTP reconstitue via la fonction otp_build_graph() un graphe des cheminements possibles à partir des données d’entrée susmentionnées. Ce graphe permet de gagner un temps substantiel pour démarrer de nouvelles sessions d’OTP, à la manière d’une sauvegarde des réseaux.

Le paramètre otp indique le chemin d’accès vers l’archive Java d’OTP (fichier OTP .jar).

Le paramètre dir indique le chemin d’accès vers le répertoire contenant les fichiers nécessaires à la génération du graphe.

Le paramètre memory prend un nombre entier qui renseigne la quantité de mémoire à attribuer à OTP (par défaut 2048 Mo). La quantité de mémoire dépend de la taille de l’espace géographique à l’étude, de la puissance de votre machine et de la version de Java utilisée (idéalement du 64 bits, vous serez limités à quelques Go si vous utilisez le 32 bits).

Le territoire métropolitain de Montpellier correspond à un espace géographique de taille intermédiaire, nous allons donc demander à utiliser 10 Go de mémoire (c’est-à-dire 10 000 Mo). Cette valeur est à définir en fonction des capacités de votre ordinateur et de votre aire d’étude.

Notons ici qu’OTP est optimisé pour réaliser des routages à l’échelle d’une ville et que ses performances se dégraderont avec de plus grandes zones.

otp_build_graph(otp = path_otp, dir = path_data, memory = 10000)

Une fois le graphe construit, il est recommandé de passer la ligne en commentaire afin de ne pas relancer la procédure à chaque exécution du code.

3.5 Synthèse de l’organisation du dossier OTP

Nous pouvons résumer l’organisation du dossier OTP ainsi :

Un fichier router-config.json, facultatif pour démarrer une session OTP, pourra être ajouté dans un second temps pour modifier des paramètres de déplacement (voir partie 4.5). Le fichier Graph.obj est lui, par contre, indispensable pour démarrer une cession OTP. Il sera créé dans l’environnement R.

Pour reproduire le prochain exemple, le répertoire OTP et les données sont disponibles sur Zenodo :


Télécharger les données

4 Lancer OTP

4.1 Générer une instance locale du serveur OTP

Il est ensuite nécessaire d’installer une instance locale du logiciel. Nous la créons grâce à la fonction otp_setup(), qui lance le calculateur d’itinéraires (default router) par défaut via une interface Web. Cette procédure peut prendre quelques minutes.

OTP dispose d’un serveur Web Grizzly intégré qui s’exécute sur les ports 8080 (http) et 8081 (https). Les options port et securePort de la fonction permettent d’indiquer des ports alternatifs si les ports par défaut sont utilisés par d’autres applications en cours.

otp_setup(otp = path_otp, dir = path_data)

La session d’OTP est alors démarrée en local, et une fenêtre s’ouvre dans un navigateur Web afin d’accéder à l’interface graphique du logiciel.

L’interface graphique de l’application Web d’OTP permet de zoomer/dézoomer sur l’espace d’étude, de spécifier des dates et des durées de déplacement à pied, le mode de transport à considérer (et d’en paramétrer l’usage), ou encore de modifier le fond de carte.

4.2 Paramétrer une requête depuis l’interface graphique

Pour rechercher un itinéraire depuis OTP, il est possible de désigner les lieux d’origine et de destination par leurs couples de coordonnées de longitude et de latitude ou de renseigner des noms d’arrêts issus des données d’entrée.

Pour définir un point de départ, sans en connaître les coordonnées de longitude/latitude ou le nom d’arrêt au préalable, il est possible de le sélectionner directement depuis la fenêtre graphique comme suit :

Notons ici l’importance des bornes temporelles de validité des fichiers GTFS dans l’obtention d’une réponse à une requête. OTP ne sera en effet pas en capacité de trouver un déplacement pour des périodes en amont ou en aval des dates renseignées dans le fichier calendar.

D’autres paramétrages sont directement accessibles depuis l’interface, comme l’illustre l’exemple ci-dessous.

4.3 Connecter R à l’instance locale d’OTP

Afin d’automatiser les procédures, il est intéressant d’exécuter OTP depuis R, en lui soumettant des lignes de commande personnalisées.

La fonction otp_connect() permet de tester et de créer une connexion à OTP depuis R. Plusieurs paramètres peuvent être passés en option, tels que le fuseau horaire (timezone) à partir duquel construire les requêtes (par défaut, la valeur est celle de votre machine), la version d’OTP (par défaut, la version renseignée est 1.5), ou encore le port utilisé pour la connexion (par défaut, le numéro de port utilisé est 8080).

my_otpcon <- otp_connect(timezone = "Europe/Paris")

4.4 Arrêter l’instance locale d’OTP

Le lancement d’OTP a automatiquement démarré Java, qui s’exécute hors de l’environnement R. Il faut donc penser à arrêter OTP et Java lorsque l’on quitte la session R à partir de laquelle une session OTP avait été lancée. On utilise pour cela la fonction otp_stop().

Un message s’affiche, afin de valider la clôture de Java induite par la commande otp_stop() : “This will force Java to close, Press [enter] to continue, [escape] to abort”. Notons ici qu’en pressant sur [enter], toutes les applications utilisant Java seront arrêtées.

otp_stop()

4.5 Configurer les options de déplacement

4.5.1 Créer manuellement les fichiers de configuration

OTP permet de configurer les options de déplacement à prendre en compte lors du déploiement de l’application (version personnalisée d’OTP) à l’aide de trois types de fichiers au format JavaScript Object Notation (.json) :

  • build-config.json renseigne les éléments indispensables à la génération du graphe, par exemple en spécifiant les fichiers GTFS sources pour différents systèmes de transports en commun fonctionnant sur le territoire ou en indiquant que l’on souhaite que les transferts entre arrêts d’une même station de transport (par exemple, de métro) soient privilégiés :

  • router-config.json contient des options de configuration d’exécution mobilisées lors du setup (i.e. otp_setup()) qui peuvent donc être modifiées une fois le graphe construit. On peut par exemple spécifier les durées maximales de recherche d’un itinéraire et de ses alternatives avec l’option timeouts ou modifier le sens de circulation par défaut sur le réseau routier par l’option driveOnRight: false :

  • otp-config.json contient des commutateurs simples qui activent ou désactivent les fonctionnalités à l’échelle du système. La plupart des applications ne nécessitent pas la modification de ce fichier.

De nombreux paramètres peuvent ainsi être ajoutés dans le fichier de configuration du serveur OTP (voir la présentation en ligne). Des exemples de fichiers de configuration sont disponibles en ligne. Notons toutefois qu’OTP fournit des valeurs par défaut qu’il définit comme raisonnables. Les trois fichiers sont donc optionnels, tout comme les options à l’intérieur de chacun.

4.5.2 Modifier les paramètres via des fonctions R

La bibliothèque opentripplanner propose plusieurs fonctionnalités afin de faciliter la configuration de l’instance OTP.

La fonction otp_make_config() crée un fichier de configuration, de type router, build ou otp.

router_config <- otp_make_config(type = "router")  

Le fichier est organisé sous la forme de quatre listes, comprenant chacune un ensemble d’objets :

  • $routingDefaults : liste la série de paramètres par défaut, par exemple la vitesse de marche ($routingDefaults$walkSpeed)

  • $boardTimes : liste les paramètres de configuration des temps d’embarquement par mode de transport

  • $alightTimes : liste les paramètres de configuration des temps de démarrage par mode de transport

  • $timeouts : liste les délais maximums de calcul des 1er, 2e, 3e, etc. itinéraires.

router_config$routingDefaults$walkSpeed <- as.numeric(0.84)
router_config$routingDefaults$bikeParkTime <- as.integer(120)

La fonction otp_validate_config() génère des tests de validité d’un l’objet de configuration. Un problème régulièrement rencontré est l’erreur de typage des paramètres de la fonction (integer, numeric, etc.).

otp_validate_config(router_config)

Une fois le paramètrage du fichier de configuration finalisé, la fonction otp_write_config() permet de l’exporter au format JSON.

otp_write_config(router_config,                
                 dir = path_data,
                 router = "default")

Les configurations du routeur sont prises en compte lors du lancement du serveur OTP et ne peuvent être mobilisées alors qu’une session est en cours. Nous allons donc nous déconnecter puis nous reconnecter à l’instance locale d’OTP pour pouvoir en tenir compte lors de nos analyses.

otp_stop()

otp_setup(otp = path_otp, dir = path_data, router = "default")

my_otpcon <- otp_connect(timezone = "Europe/Paris", router = "default")

5 Création d’itinéraires

Les recherches d’itinéraires constituent un premier levier d’analyse de l’accessibilité d’un lieu ou d’un ensemble de lieux depuis un ou plusieurs points de départ.

5.1 Requête unique

La fonction otp_plan() permet d’obtenir un ou des itinéraires connectant un point d’origine (fromPlace) à un point d’arrivée (toPlace). Tout comme sur l’interface graphique, les lieux sont définis par un couple de coordonnées de longitude et de latitude, à la différence qu’ici les latitudes sont renseignées en premier et les longitudes ensuite. On combine classiquement ces deux valeurs dans un vecteur via la fonction c() (combine).

Notons que pour utiliser des noms d’arrêts comme lieu de départ et d’arrivée, il faut remplacer les arguments fromPlace/toPlace par les arguments fromID/toID.

L’argument otpcon renseigne l’objet de connexion OTP produit avec la fonction otp_connect().

L’argument mode désigne les modes de transport à considérer pour le calcul de l’itinéraire : TRANSIT (déplacement en transport en commun, tous modes confondus), BUS (déplacement en bus), TRAM (déplacement en tramway), RAIL (déplacement en train), WALK (déplacement piéton), BICYCLE (déplacement à vélo), CAR (déplacement en voiture), etc. Par défaut, le déplacement est calculé pour un voyage en voiture.

Il est possible d’indiquer des combinaisons de modes de déplacement (parmi les options proposées par OTP). Par exemple, une combinaison c("CAR", "TRANSIT") permet de décrire des déplacements de type kiss and ride, où un usager est déposé en voiture à une station de transport en commun sans avoir lui-même à garer le véhicule automobile. La combinaison c("CAR_PARK", "TRANSIT") permet quant à elle de décrire des déplacements pour lesquels l’usager se déplace en voiture jusqu’à un parking relais à proximité d’un arrêt de transport en commun puis emprunte ce réseau.

Le paramètre date_time renseigne la date et l’heure de départ au format POSIXct. Par défaut, la date et le jour courants sont utilisés.

D’autres paramètres optionnels sont proposés, parmi lesquels :

  • maxWalkDistance qui indique la distance maximale (en mètres) à parcourir à pied sur l’ensemble du déplacement

  • arriveBy qui permet de définir l’heure d’arrivée (arriveBy = TRUE) via le paramètre date_time plutôt que l’heure de départ (par défaut, arriveBy = FALSE)

  • numItineraries qui permet de restreindre le nombre d’itinéaires en sortie.

route <- otp_plan(otpcon = my_otpcon, 
                  fromPlace = c(3.87943, 43.60844), 
                  toPlace = c(3.96915, 43.54009),
                  mode = "BICYCLE",
                  date_time = as.POSIXct("2023-04-19 10:15:00"))

Le résultat de la requête est un tableau de données (dataframe) décrit au format simple features de la bibliothèque du même nom (sf).

Le déplacement proposé est composé de trois étapes (legs), chacune étant caractérisée par un mode de transport, recensées dans l’ordre du cheminement. La somme de leurs distances et de leurs durées correspond ainsi à la distance (en mètres) et à la durée (en secondes) du déplacement total :

sum(route$leg_distance)
[1] 11833.17
sum(route$leg_duration)
[1] 2713

5.2 Requêtes multiples

L’intérêt d’instruire des requêtes OTP par lignes de commande est certainement de pouvoir générer des demandes multiples, par exemple, en étudiant les temps de déplacement nécessaires pour se déplacer d’une origine à une destination à différents moments de la journée, en définissant une liste de lieux de départ pour un même lieu d’arrivée ou encore pour des plages horaires différenciées.

Le format des paramètres fromPlace et toPlace peut être varié : couple de paires de longitude/latitude, matrice à deux colonnes décrivant les paires de longitude/latitude ou encore objet sf contenant des géométries ponctuelles décrites dans le système de coordonnées WGS 84 (EPSG 4326).

coord_origine <- as.matrix(rbind(c(3.86459, 43.69760), c(4.01288, 43.66112)))

coord_dest <- c(3.89695, 43.59880)

route_2 <- otp_plan(otpcon = my_otpcon, 
                  fromPlace = coord_origine, 
                  toPlace = coord_dest,
                  mode = c("TRANSIT", "WALK"),
                  date_time = as.POSIXct("2023-04-19 10:15:00"))

La localisation des déplacements peut aisément être obtenue et cartographiée. L’exemple ci-dessous utilise la fonction annotation_map_tile() de la bibliothèque ggspatial afin d’ajouter un fond à la carte générée via la fonction geom_sf() de la bibliothèque ggplot2. Nous activerons également la bibliothèque prettymapr qui est reliée à ggspatial pour afficher cette fonction. La fonction annotation_map_tile() est accompagnée du paramètre data = route_2 afin de centrer la carte sur les données spatiales. Par défaut, le fond de carte affiché est celui d’OpenStreetMap. La liste des fonds de carte disponibles est accessible via la fonction rosm::osm.types().

library(ggplot2)
library(ggspatial)
library(prettymapr)

ggplot() +
  annotation_map_tile (data = route_2) +
  geom_sf(data = route_2, col = "red", linewidth = 1) +
  annotate(geom = "point", x = coord_dest[1], y = coord_dest[2], colour = "black", size = 2)

L’usage de l’argument get_geometry = FALSE dans la fonction otp_plan() permet, à l’inverse, d’abandonner leur géométrie. Cette action permet notamment de gagner en temps de calcul lorsque les lieux sur lesquels opérer les requêtes sont nombreux et que l’on s’intéresse aux attributs des déplacements.

route_3 <- otp_plan(otpcon = my_otpcon, 
                  fromPlace = coord_origine, 
                  toPlace = coord_dest,
                  mode = c("TRANSIT", "WALK"),
                  date_time = as.POSIXct("2023-04-19 10:15:00"),
                  get_geometry = FALSE)
library(dplyr)

route_3b <- route_3 %>% 
  distinct(fromPlace, route_option, .keep_all = TRUE) %>%
  select(fromPlace, toPlace, duration) 

library(tidyr)

time_matrix <- pivot_wider(data = route_3b, names_from = toPlace, values_from = duration) 

On peut également choisir de ne garder au sein de la matrice que la durée de déplacement minimale pour chaque couple origine/destination. L’usage des coordonnées géographiques pour désigner les lieux d’origine et de destination n’est ni usuel, ni pratique. Nous allons donc également les modifier.

route_3c <- route_3 %>% 
  distinct(fromPlace, route_option, .keep_all = TRUE) %>%
  select(fromPlace, toPlace, duration) %>%
  group_by(fromPlace) %>% slice(which.min(duration))

route_3c$fromPlace <- c("Ori_1", "Ori_2")

route_3c$toPlace <- "Dest_1"

time_matrix_2 <- pivot_wider(data = route_3c, 
                             names_from = toPlace, 
                             values_from = duration)

Ces matrices peuvent par exemple être retranscrites graphiquement sous la forme de cercles proportionnels au temps de déplacement entre les lieux.

# passage de la matrice au format long pour faciliter sa mise en page 
# graphique via la fonction melt de la bibliothèque reshape2
library(reshape2)

tabCont_time_matrix_2 <- melt(time_matrix_2) 

# construction d'un graphique en cercles proportionnels 
# avec la bibliothèque ggplot2
library(ggplot2)

ggplot(tabCont_time_matrix_2,
       aes(x = variable,
           y = fromPlace)) +  
  geom_point(aes(size = value), color = "blue")  +
  scale_size(range = c(2, 4),   
             breaks = c(4431, 4981), 
             labels = c("4431", "4981"), 
             name = "Temps (sec)",
             guide = "legend") +
  labs(title = "Durée estimée des déplacements", 
       x = "Destination",
       y = "Origines") +
  theme_minimal() 

5.3 Cartes d’itinéraires interactives

Le code ci-dessous présente un exemple de cartographie interactive réalisée avec la bibliothèque leaflet pour R.

Nous représenterons le déplacement calculé dans l’objet route de la partie 5.1. Pour centrer la carte, nous allons dans un premier temps utiliser les données géospatiales décrivant les contours de la métropole de Montpellier. Le package leaflet fonctionnant avec des données non projetées, nous utilisons la fonction st_transform() de la librairie sf afin de transformer le système de projection de la couche.

Les fonctions st_centroid() et st_coordinates() de la librairie sf permettent respectivement de définir le centroïde du polygone et d’en récupérer les coordonnées au sein d’une matrice. Les longitudes sont données dans la première colonne et les latitudes dans la seconde.

Notons que le GeoJSON epci_3m a été téléchargé et chargé dans la partie 2.2.1.

epci_3m_4326 <- st_transform(epci_3m, 4326)

centro_3m <- st_centroid(epci_3m_4326)

lat_centro_3m <- median(st_coordinates(centro_3m)[,2])

lon_centro_3m <- median(st_coordinates(centro_3m)[,1])

Nous faisons ensuite appel aux fonctionnalités de la bibliothèque leaflet afin de construire la carte interactive de l’objet route que nous venons de créer :

  • setView() permet de définir la vue de départ, en indiquant son centre géographique par un couple de coordonnées et un niveau de zoom allant de 0 (monde) à 18 (rue)

  • addPolylines() permet d’ajouter des objets géographiques de type polylignes. Par défaut, les géométries sont celles des données renseignées dans la fonction leaflet de génération de la carte interactive (ici l’objet route au format sf). On peut choisir d’ajouter des fenêtres contextuelles (pop-up), par exemple sous la forme d’une étiquette apparaissant au passage du curseur sur l’objet. Parmi les nombreux paramètres de mise en forme disponibles, color et weight permettent respectivement de contrôler la couleur et l’épaisseur (en pixels) des traits

  • addProviderTiles() permet d’ajouter un fond de carte interactif d’un des principaux fournisseurs (OpenStreeMap, CartoDB, Stamen, Thunderforest, etc.). La liste complète est accessible via la commande names(providers).

library(leaflet)

leafMap <- leaflet(data = route) %>%
  setView(lat = lat_centro_3m,
          lng = lon_centro_3m,
          zoom = 10) %>%
  addPolylines(popup = ~paste("Durée : ", round(duration/60)," minute(s)"),
               color = "#e31a1c",
               weight = 4) %>%
  addProviderTiles(providers$CartoDB.Positron)

leafMap

Nous pouvons également choisir de représenter les étapes du déplacement par des couleurs différenciées selon le mode de transport. Dans un premier temps, nous associons une couleur (de la palette Set1 proposée dans la bibliothèque RColorBrewer) par mode. Il est possible de visualiser plus en détail la deuxième étape du déplacement en entrant des coordonnées géographiques définies dans le paramètre setView (setView(lat = 43.608887, lng = 3.880381)) et en augmentant le niveau de zoom (zoom = 18).

mycol <- colorFactor(
  palette = 'Set1',
  domain = route$leg_mode
)

leafMap <- leaflet(data = route) %>%
  setView(lat = 43.608887,
          lng = 3.880381,
          zoom = 18) %>%
  addPolylines(popup = ~paste("Durée : ", round(duration/60)," minute(s)"),
               color = ~mycol(leg_mode),
               weight = 4) %>%
  addProviderTiles(providers$CartoDB.Positron)

leafMap

Nous allons enfin choisir d’adapter le contenu des fenêtres contextuelles aux étapes du déplacement, par exemple en affichant le mode de transport et la durée de l’étape. Pour séparer ces deux types de contenus informationnels (que nous afficherons en gras grâce à la balise HTML <strong></strong>), nous utiliserons la balise HTML <br> (break line) afin de créer un saut de ligne.

leafMap <- leaflet(data = route) %>%
  setView(lat = 43.608887,
          lng = 3.880381,
          zoom = 18) %>%
  addPolylines(popup = ~paste("<strong>Durée de l'étape :</strong> ",
                              round(leg_duration/60),
                              " minute(s)<br>","<strong>Mode :</strong>", 
                              leg_mode),
               color = ~mycol(leg_mode),
               weight = 4) %>%
  addProviderTiles(providers$CartoDB.Positron)

Cliquer sur les tronçons pour obtenir les informations :

leafMap

6 Analyses d’accessibilité sous contrainte temporelle

Nous proposons dans ce tutoriel deux types d’analyse d’accessibilité multimodale sous contrainte temporelle parmi l’ensemble des possibilités envisageables :

  • la cartographie d’isochrones de temps d’accès à un lieu et à un horaire donné

  • la cartographie du nombre de déplacements réalisables et des temps moyens d’accès à un lieu pour une plage horaire donnée.

6.1 Isochrones d’accessibilité

Les cartes d’isochrones délimitent des aires d’accessibilité équivalentes en temps de déplacement à partir d’un point de départ. Elles figurent généralement plusieurs zones d’accessibilité, afin de représenter les différentiels croissants de coût du déplacement à mesure que l’on s’éloigne du lieu d’intérêt.

6.1.1 Localiser les différentiels d’accès à un lieu selon un mode de déplacement

La fonction otp_isochrone() génère des cartes d’isochrones en fonction d’un mode de transport (ou de la combinaison de plusieurs modes). Les paramètres de la fonction sont identiques à ceux utilisés pour générer des itinéraires, à l’exception de l’argument cutoffSec qui permet de définir les bornes maximales de chaque intervalle de temps, en secondes (les valeurs doivent donc être multipliées par 60 pour représenter des temps en minutes, par 3 600 pour représenter des temps en heures, etc).

iso_gareStRoch <- otp_isochrone(otpcon = my_otpcon,
            fromPlace = c(3.880727, 43.604937),
            mode = c("WALK", "TRANSIT"),
            maxWalkDistance = 2000,
            date_time = as.POSIXct("2023-04-25 08:00:00"),
            cutoffSec = c(15, 30, 45, 60, 75, 90) * 60) 

Nous pouvons utiliser les fonctionnalités de cartographie interactive proposées par la bibliothèque leaflet pour R afin de donner à voir le résultat. Pour retrouver des temps de déplacement en minutes, nous créons une variable transformant le temps de déplacement associé à l’isochrone (donné en secondes) en le divisant par 60.

iso_gareStRoch$minutes = iso_gareStRoch$time / 60 

Les isochrones de distance représentant des intervalles de temps de déplacement, nous allons d’abord construire la variation de teintes représentant ces différences d’ordre de grandeur.

mycol_2 <- colorFactor(
  palette = 'Reds',
  domain = iso_gareStRoch$minutes
)

Nous utiliserons la fonction addPolygons() afin de figurer les isochrones sur le fond de carte, en introduisant de nouvelles options de mise en forme :

  • stroke = FALSE : permet de retirer les contours des polygones

  • smoothFactor : définit le niveau de simplication des géométries en fonction du zoom

  • fillOpacity : contrôle le niveau d’opacité (resp. de transparence) du remplissage.

Avant de créer la carte, nous téléchargeons les lignes de tramway de la métropole de Montpellier (format shapefile) afin de faciliter l’interprétation des résultats :


Télécharger les données

tramway <- st_read("data/MMM_MMM_LigneTram.shp")
tramway <- st_transform(tramway, 4326)
leafMap <- leaflet(data = iso_gareStRoch) %>%
  setView(lat = 43.604937, lng = 3.880727, zoom = 10) %>%
  addPolygons(stroke = FALSE,
              color = ~mycol_2(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1) %>%
  addProviderTiles(providers$CartoDB.Positron)

leafMap <- leafMap %>%
  addPolylines(data = tramway,
               opacity = 1,
               weight = 4,
               color = ~factor(num_exploi, levels = c(1, 2, 3, 4), 
                               labels = c("blue", "#c51b8a", "green", "brown")))

leafMap

Nous allons également ajouter une légende afin de faciliter la lecture de la carte.

mycol_3 <- colorFactor(
  palette = c("blue", "#c51b8a", "darkgreen", "brown"),
  domain = tramway$num_exploi)
leafMap <- leaflet(data = iso_gareStRoch) %>%
  setView(lat = 43.604937, lng = 3.880727, zoom = 10) %>%
  addPolygons(stroke = FALSE,
              color = ~mycol_2(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addLegend("bottomright", pal = mycol_2, 
            values = ~minutes,
    title = "Temps d'accès",
    labFormat = labelFormat(suffix = " min"),
    opacity = 1)

leafMap <- leafMap %>%
  addPolylines(data = tramway,
               opacity = 1,
               weight = 4,
               color = ~factor(num_exploi, levels = c(1, 2, 3, 4), 
                               labels = c("blue", "#c51b8a", 
                                          "darkgreen", "brown"))) %>%
  addLegend("bottomleft", pal = mycol_3,
            values = tramway$num_exploi,
            title = "Lignes de tramway",
            opacity = 1)

leafMap

6.1.2 Comparer cartographiquement les différentiels d’accès à un lieu entre deux modes

Nous pouvons aussi choisir de superposer les isochrones de temps de déplacement à vélo sur la carte, et proposer à l’utilisateur de basculer d’une représentation à une autre grâce au paramètre group proposé par leaflet.

iso_gareStRoch_velo <- otp_isochrone(otpcon = my_otpcon,
            fromPlace = c(3.880727, 43.604937),
            mode = "BICYCLE",
            maxWalkDistance = 2000,
            date_time = as.POSIXct("2023-04-25 08:00:00"),
            cutoffSec = c(15, 30, 45, 60, 75, 90) * 60) 

iso_gareStRoch_velo$minutes = iso_gareStRoch_velo$time / 60 

Pour distinguer les deux modes de déplacement sur la carte, nous construisons une progression chromatique en teintes bleues :

mycol_4 <- colorFactor(
  palette = 'Blues',
  domain = iso_gareStRoch_velo$minutes)

À l’aide de l’option group et des fonctionnalités de contrôle des couches introduites par la fonction addLayersControl(), il est possible de modifier l’aspect de la carte en fonction des choix d’affichage de l’utilisateur.

leafMap <- leaflet() %>%
  setView(lat = 43.604937, lng = 3.880727, zoom = 10) %>%
    addPolygons(data = iso_gareStRoch_velo,
              stroke = FALSE,
              color = ~mycol_4(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1, 
              group = "VELO") %>%
  addPolygons(data = iso_gareStRoch,
              stroke = FALSE,
              color = ~mycol_2(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1, 
              group = "TRANSIT") %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addLayersControl(
    overlayGroups = c("TRANSIT", "VELO", "TRAMWAY"),
    options = layersControlOptions(collapsed = FALSE))

leafMap <- leafMap %>%
  addPolylines(data = tramway,
               opacity = 1,
               weight = 4,
               group = "TRAMWAY",
               color = ~factor(num_exploi, levels = c(1, 2, 3, 4), 
                               labels = c("blue", "#c51b8a", "green", "brown")))

leafMap

L’affichage de la légende peut également être rendu dépendant des fonctionnalités de contrôle des couches via le paramètre group.

leafMap <- leaflet() %>%
  setView(lat = 43.604937, lng = 3.880727, zoom = 10) %>%
  addPolygons(data = iso_gareStRoch_velo,
              stroke = FALSE,
              color = ~mycol_4(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1, 
              group = "VELO") %>%
  addPolygons(data = iso_gareStRoch,
              stroke = FALSE,
              color = ~mycol_2(minutes),
              smoothFactor = 0.2,
              fillOpacity = 1, 
              group = "TRANSIT") %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addLayersControl(
    overlayGroups = c("TRANSIT", "VELO", "TRAMWAY"),
    options = layersControlOptions(collapsed = FALSE)
  ) %>%
  addLegend("bottomright", pal = mycol_2, 
            values = iso_gareStRoch$minutes,
            title = "Temps d'accès (TC/marche)",
            labFormat = labelFormat(suffix = " min"),
            opacity = 1,
       group = "TRANSIT"
  ) %>%
  addLegend("bottomleft", pal = mycol_4, 
            values = iso_gareStRoch_velo$minutes,
    title = "Temps d'accès (vélo)",
    labFormat = labelFormat(suffix = " min"),
    opacity = 1,
    group = "VELO")

leafMap <- leafMap %>%
  addPolylines(data = tramway,
               opacity = 1,
               weight = 4,
               group = "TRAMWAY",
               color = ~factor(num_exploi, levels = c(1, 2, 3, 4), 
                               labels = c("blue", "#c51b8a", 
                                          "darkgreen", "brown"))) %>%
  addLegend("bottomleft", pal = mycol_3,
            values = tramway$num_exploi,
            title = "Lignes de tramway",
            opacity = 1,
            group = "TRAMWAY")
  
leafMap

6.2 Nombre de déplacements réalisables dans un intervalle de temps

L’analyse proposée ici vise à qualifier l’offre de transport en commun entre une ou plusieurs origine(s) en direction d’une destination à partir de la mesure du nombre de déplacements réalisables et de leurs durée. Cette analyse d’accessibilité sous contrainte horaire s’inscrit dans le cadre théorique et méthodologique de la time geography (par exemple : Chapelon 2016  ; Conesa 2012  ; L’Hostis et al. 2004).

À titre d’exemple, nous développons un programme permettant de compter tous les déplacements possibles via le réseau de la TAM à partir des 30 mairies de la métropole (hors Montpellier) pour une arrivée entre 7h et 9h à la gare Saint-Roch. L’objectif est d’évaluer la qualité de l’offre de transport théorique à l’heure de pointe du matin à partir des centres-villes des communes métropolitaines (les retards potentiels liés aux accidents de circulation, aux travaux, etc. ne sont pas pris en compte).

6.2.1 Préparation des données

La localisation des 30 mairies de la métropole de Montpellier (hors Montpellier) est recueillie à partir de la base de données des équipements de l’INSEE. Les mairies correspondent aux lignes portant le code A129 pour la variable TYPEQU. Dans ce tutoriel, nous utilisons une extraction de la base de données réalisée a priori, du fait de la lourdeur du fichier d’origine.

Les données utilisées dans cet exemple sont disponibles ici :


Télécharger les données

mairies <- read.csv("data/mairies_3M.csv", header = TRUE)

head(mairies)
  AAV2020   AN BV2012 DEP DEPCOM DOM      EPCI    DCIRIS LAMBERT_X LAMBERT_Y
1      12 2021  34022  34  34022   A 243400017 340220102  781729.9   6285175
2      12 2021  34058  34  34027   A 243400017 340270000  782250.0   6292750
3      12 2021  34172  34  34057   A 243400017 340570103  772475.6   6281921
4      12 2021  34058  34  34058   A 243400017 340580101  779478.3   6287040
5      12 2021  34172  34  34077   A 243400017 340770101  771637.0   6284634
6      12 2021  34088  34  34087   A 243400017 340870000  757131.6   6272537
   QP QUALI_IRIS QUALI_QP QUALI_QVA QUALI_ZFU QUALI_ZUS QUALITE_XY QVA REG SDOM
1 CSZ          1        X         X         X         X      Bonne CSZ  76   A1
2 CSZ          X        X         X         X         X Acceptable CSZ  76   A1
3 CSZ          1        X         X         X         X      Bonne CSZ  76   A1
4 CSZ          1        X         X         X         X      Bonne CSZ  76   A1
5 CSZ          1        X         X         X         X      Bonne CSZ  76   A1
6 CSZ          X        X         X         X         X      Bonne CSZ  76   A1
  TYPEQU UU2020 ZFU ZUS
1   A129  34302 CSZ CSZ
2   A129  34133 CSZ CSZ
3   A129  34701 CSZ CSZ
4   A129  34206 CSZ CSZ
5   A129  34701 CSZ CSZ
6   A129  34214 CSZ CSZ

Nous transformons la couche d’informations géographiques décrivant les mairies au format dataframe en simple feature dataframe, c’est-à-dire dans un format explicitement géographique grâce à la fonction st_as_sf.

mairies <- st_as_sf(mairies,
                    coords = c("LAMBERT_X", "LAMBERT_Y"),
                    crs = st_crs(2154))

Les coordonnées géographiques étant en Lambert93 (code EPSG 2154), nous les traduisons pour OTP en WGS84 (code EPSG 4326) via la fonction st_transform.

mairies <- st_transform(mairies, 4326)

Nous choisissons la plage horaire pour laquelle OTP réalisera toutes ses requêtes. L’horaire de départ est 6h et l’horaire de fin 9h afin de nous assurer de récolter les déplacements permettant d’arriver entre 7h et 9h à la gare Saint-Roch.

temps_debut <- as.POSIXct("2023-04-19 06:00:00")

temps_limite <- as.POSIXct("2023-04-19 09:00:00")

Nous créons ensuite les listes de lieux d’origine et de destination.

coord_origine <- as.matrix(cbind(st_coordinates(mairies)[,1], st_coordinates(mairies)[,2]))

coord_dest <- c(3.88040, 43.60508)

6.2.2 Exécution de la boucle pour récolter l’offre de transport

Nous construisons une boucle qui réalise une itération par minute supplémentaire de l’horaire de début jusqu’à l’horaire limite au cours de laquelle nous effectuons des requêtes d’itinéraires en transport en commun depuis les lieux d’origine vers le lieu de destination dont nous enregistrons successivement les caractéristiques dans un tableau de données que nous nommons routes.

La durée de cette opération est fonction du nombre de traitements et d’itérations qu’OTP doit réaliser. La fonction tryCatch() permet à la boucle de fonctionner malgré une éventuelle erreur de connexion au serveur OTP, due à des instabilités ponctuelles.

routes <- data.frame()

while(temps_debut < temps_limite){
  tryCatch({
    resultats <- otp_plan(otpcon = my_otpcon, 
                          fromPlace = coord_origine,
                          toPlace = coord_dest,
                          mode = c("TRANSIT", "WALK"),
                          date_time = temps_debut,
                          maxWalkDistance = 1000,
                          get_geometry = FALSE)
    temps_debut <- temps_debut + 60
    routes <- rbind(routes, resultats)
  }, error = function(e) {
    print(paste("Erreur dans la boucle :", e$message))
  })
}

6.2.3 Mise en forme des données

Puisque nous voulons compter le nombre de déplacements possibles avec une arrivée à destination entre 7h et 9h, la période de pointe, nous supprimons les déplacements hors-champ (arrivée avant 7h et après 9h).

routes <- routes[routes$endTime >= "2023-04-19 07:00:00" & routes$endTime < "2023-04-19 09:00:00", ]

La boucle d’itérations a récolté un grand nombre de doublons : d’une minute à une autre, l’offre de transport renvoyée par OTP ne change pas systématiquement. De ce fait, nous sélectionnons les seuls déplacements uniques renvoyés par le serveur OTP avec la commande distinct. Le paramètre .keep_all = TRUE permet de garder l’ensemble des colonnes de routes.

tableau_resultat <- routes %>%
  distinct(fromPlace, endTime, .keep_all = TRUE)

Enfin, nous calculons le nombre de déplacements à partir de chaque mairie avec la commande n_distinct(endTime), et la durée moyenne des déplacements avec mean(duration/60).

tableau_resultat <- tableau_resultat %>%
  group_by(fromPlace) %>%
  summarise(nb_deplacements = n_distinct(endTime), 
            duree_moy = mean(duration/60))

Le graphique ci-dessous représente la distribution des temps de déplacement moyens obtenus pour les différentes communes sous la forme d’un histogramme.

ggplot() + geom_histogram(aes(x = tableau_resultat$duree_moy),
                          bins = 9,
                          color = "black", 
                          fill = "lightblue") +
    labs(title = "Distribution des durées moyennes des déplacements obtenus pour les 30 communes",
         x = "Durée moyenne des déplacements (min)",
         y = "Nombre de mairies") +
    theme_classic() # thème de fond (ici "classic")

Nous notons que la répartition des temps de déplacement moyens en transport en commun depuis les mairies des communes de la métropole de Montpellier Méditerrannée vers la gare Saint-Roch aux heures de pointe du matin est très irrégulière et présente des écarts importants (les valeurs étant comprises entre 30 et 84 minutes).

6.2.4 Cartographie

Nous souhaitons savoir si les différences de temps de déplacement moyens s’organisent en fonction d’une logique d’éloignement géographique des communes à la ville centre. Pour cela, nous transformons préalablement l’objet tableau_resultat au format dataframe vers le format simple feature dataframe, en mobilisant les coordonnées géographiques des différents points de départ obtenues en extrayant le contenu de la colonne fromPlace via la fonction word().

library(stringr)

tableau_resultat$x <- word(tableau_resultat$fromPlace, 1, sep = ",")

tableau_resultat$y <- word(tableau_resultat$fromPlace, 2, sep = ",")

tableau_resultat <- st_as_sf(tableau_resultat, coords = c("y", "x"),
                    crs = st_crs(4326))

Nous choisissons de représenter ces différentiels d’accessibilité sous forme d’une cartographie statique. Le nombre de déplacements réalisables par commune est figuré par le biais de cercles proportionnels. Les durées moyennes de ces déplacements sont discrétisées par classes d’intervalles égaux.

De nombreuses bibliothèques existent pour réaliser de la cartographie avec R, dont la bibliothèque phare de création de graphiques avec R ggplot2.

mapsf a toutefois été créée en 2021 dans le but spécifique de faciliter la production de cartes thématiques conformes au règle de la sémiologie graphique avec R, à partir d’objets sf. Les principales fonctionnalités de la bibliothèque sont recensées dans un document graphique en ligne.

La fonction mf_map() de la bibliothèque mapsf est la fonction centrale du package qui permet de construire une carte au format varié à partir des paramètres :

  • x l’objet au format sf

  • var la variable à cartographier

  • type le type de carte que l’on souhaite créer (prop pour des symboles proportionnels, choro pour une carte choroplèthe, typo pour une palette de couleurs variées, symb pour des symboles ponctuels différenciés…). Puisque nous souhaitons représenter conjointement une variable quantitative de stock et des intervalles de temps de déplacement, nous choisissons le type prop_choro qui permet de varier la taille des figurés et leur intensité chromatique de façon synchrone.

Nous ajontons le nombre d’habitants pour chaque commune (INSEE RP2020) et calculons la densité de population. La surface de chaque polygone est calculée avec st_area avec l’unité m\(^2\) par défaut. Nous utilisons la bibliothèque units pour convertir automatiquement les surfaces de chaque commune de m\(^2\) à km\(^2\) avec set_units. L’expression attributes() = NULL permet ensuite de supprimer les unités de la colonne car elles peuvent causer des problèmes lors des rendus graphiques (comme ggplot2).

library(units)

epci_3m$popu <- c("9305", "6794", "2176", "23469", "4014", "1640",
                  "5613", "2839", "6423", "9438", "6490", "3293",
                  "7194", "8218", "12104", "2818", "3517", "7600", 
                  "2183", "1039", "299096", "6007", "5628", "11843",
                  "10463", "8885", "17674", "6771", "2041", "1856", "3330")

epci_3m$popu <- as.numeric(epci_3m$popu)

epci_3m$surface_km2 <- st_area(epci_3m)

epci_3m$surface_km2 <- set_units(epci_3m$surface_km2, km^2)

epci_3m$densite <- epci_3m$popu/epci_3m$surface_km2

attributes(epci_3m$densite) = NULL

Notons que la cartographie paginée nécessite d’utiliser des coordonnées définies dans un système de projection cartographique adapté au territoire à l’étude, ici nous utiliserons le Lambert93 (code EPSG 2154).

tableau_resultat <- st_transform(tableau_resultat, 2154)
epci_3m <- st_transform(epci_3m, 2154)

gare_st_Roch <- data.frame(x = 3.880731, y = 43.604622)
gare_st_Roch <- st_as_sf(gare_st_Roch, coords = c("x", "y"), crs = 4326)
gare_st_Roch <- st_transform(gare_st_Roch, 2154)
gare_st_Roch$nom <- "Gare Saint-Roch"

library(mapsf)

mf_theme("iceberg")

mf_map(epci_3m,
       type = "choro",
       var = "densite",
       breaks = "geom",
       nbreaks = 5,
       pal = "Reds",
       leg_pos = "left",
       leg_title = "Densité de population\n(habitants par km²)",
       leg_frame = TRUE, 
       expandBB = c(0, 0, 0.1, 0))
       
mf_map(tableau_resultat,
       type = "prop_choro",
       var = c("nb_deplacements", "duree_moy"),
       inches = 0.15,
       border = "grey50",
       lwd = 1,
       leg_pos = c("right"),
       leg_title = c("Nombre", "Temps moyen\n(en min)"),
       breaks = "equal",
       nbreaks = 9,
       pal = "Greens",
       leg_frame = c(TRUE, TRUE))

mf_base(gare_st_Roch,
       add = TRUE,
       col = "blue",
       cex = 2,
       pch = 15)

mf_label(gare_st_Roch,
         var = "nom",
         col = "blue",
         halo = TRUE,
         cex = 1,
         bg = "white",
         pos = c(4, 1),
         r = 0.05)

mf_layout(
  title = "Déplacements des 30 mairies vers la gare Saint-Roch (7h-9h)",
  credits = paste0(
    "Sources: OTP et TAM 2022 \n",
    "mapsf ",
    packageVersion("mapsf")
  ),
  frame = TRUE)

Il est possible d’évaluer la performance de l’offre de transport par rapport à la distance euclidienne qui sépare les mairies de la gare Saint-Roch. Nous pouvons calculer cette distance avec l’expression st_distance de la bibliothèque sf. Nous ajoutons ensuite les distances dans notre tableau_resultat.

distances <- st_distance(tableau_resultat$geometry, gare_st_Roch$geometry)

tableau_resultat$distance_euclidienne <- distances

Afin de faciliter la lecture des résultats, il nous faut les codes INSEE de chacune des mairies. Les codes INSEE sont enregistrés dans le tableau mairies avec les même coordonnées géographiques que tableau_resultat. Nous devons réaliser une jointure spatiale pour joindre toutes les colonnes des deux tableaux avec la fonction st_join. Nous devons au préalable attribuer la valeur de 0.1 à st_precision afin de faire correspondre les coordonnées géographiques des deux tableaux en raison de très légères différences (aussi petites soient-elles) dues à des manipulations antérieures.

mairies <- st_transform(mairies, 2154)

st_precision(mairies) <- 0.1

st_precision(tableau_resultat) <- 0.1

jointure_spatiale <- st_join(tableau_resultat, mairies)

La conversion de mètre à kilomètre est réalisée par set_units puis nous supprimons l’unité avec attributes() = NULL car elle empêche la création du nuage de points dans ggplot2.

jointure_spatiale$distance_euclidienne <- set_units(jointure_spatiale$distance_euclidienne, km)

attributes(jointure_spatiale$distance_euclidienne) = NULL

Nous ponvons enfin réaliser le nuage de points afin de constater la performance, pour chaque mairie, de l’offre de transport par rapport à la distance qui la sépare de la gare. Nous utilisons la bibliothèque ggrepel pour que les étiquettes s’affichent avec plus de lisibilité, grâce à l’expression geom_text_repel qui empêche leur chevauchement.

library(ggrepel)

dist <- ggplot(jointure_spatiale, aes(x = distance_euclidienne, y = duree_moy, label = DEPCOM)) + 
  geom_point() +
  geom_text_repel(size = 3, box.padding = 0.3) +
  xlab("Distance euclidienne (km)") +
  ylab("Durée moyenne des déplacements (min)") +
  labs(subtitle ="Performance des transports collectifs par rapport à la distance")

dist

Le graphique permet de constater qu’il y a globalement bien une relation entre la distance et la durée moyenne des déplacements (plus c’est loin, plus il faudra de temps). Toutefois, des différences importantes de temps de déplacement, à distance égale, existent : la mairie du Crès (34090) est presque aussi éloignée que celle de Pérols (34198) alors qu’un écart de 25 minutes de déplacement les différencie (Pérols possède une desserte en tramway contrairement au Crès).

N’oubliez pas d’arrêter le serveur OTP une fois vos analyses terminées!

otp_stop()

7 Conclusion et perspectives

L’outil OTP et son intégration dans R permettent donc de mobiliser facilement les nouvelles données de l’offre de transport et de calcul d’itinéraires multimodaux pour porter un regard affiné sur l’accessibilité territoriale, à l’aide d’indicateurs d’analyses éprouvés en géographie et en aménagement : matrices O/D, isochrones de distance, nombre de déplacements possibles sous contrainte horaire, etc. De nombreuses perspectives sont ouvertes à l’issue de ce travail, telles que l’ajout d’autres opérateurs de transport collectif pour compléter l’offre de la TAM ou encore l’addition d’un modèle numérique de terrain afin de tenir compte de la pénibilité induite par certaines pentes dans le cas des déplacements à vélo.

Bibliographie

CHAPELON, Laurent, 2016. Evaluation de la performance des chaînes intermodales de transport par les mesures d’accessibilité. In : CHAPELON, Laurent (éd.), Transports et intermodalité. S.l. : ISTE Editions. pp. 107‑135.
CONESA, Alexis, 2012. Accessibilités et discontinuités spatio-temporelles multiscalaires en Nord Pas de Calais. In : Territoire en mouvement [en ligne]. novembre 2012. n° 16, pp. 18‑37. DOI 10.4000/tem.1867. Disponible à l'adresse : https://doi.org/10.4000/tem.1867.
DIJKSTRA, Edsger W., BEAUGUITTE, Laurent et MAISONOBE, Marion, 2021. E.W. Dijkstra, 1959, A Note on Two Problems in Connexion with Graphs. Numerische Mathematik [en ligne]. 2021. S.l. : s.n. Disponible à l'adresse : https://hal.science/hal-03171590.
HART, Peter, NILSSON, Nils et RAPHAEL, Bertram, 1968. A Formal Basis for the Heuristic Determination of Minimum Cost Paths. In : IEEE Transactions on Systems Science and Cybernetics [en ligne]. 1968. Vol. 4, n° 2, pp. 100‑107. DOI 10.1109/tssc.1968.300136. Disponible à l'adresse : https://doi.org/10.1109/tssc.1968.300136.
HERFORT, Benjamin, LAUTENBACH, Sven, ALBUQUERQUE, João Porto de, ANDERSON, Jennings et ZIPF, Alexander, 2022. Investigating the digital divide in OpenStreetMap: spatio-temporal analysis of inequalities in globalurban building completeness [en ligne]. août 2022. S.l. : s.n. Disponible à l'adresse : https://www.researchsquare.com/article/rs-1913150/v1.
L’HOSTIS, Alain, MENERAULT, Philippe et DECOUPIGNY, Christophe, 2004. Assessing Spatial Planning Policy with Accessibility Indicators: The Case of Lille’s Metropolis Scenario. In : Advances in Spatial Science [en ligne]. S.l. : Springer Berlin Heidelberg. pp. 293‑312. Disponible à l'adresse : https://doi.org/10.1007/978-3-540-24827-9_15.
MERICSKAY, Boris, 2017. Introduction aux données OpenStreetMap (Structuration, interrogation, extraction et édition) [en ligne]. 2017. S.l. : s.n. Disponible à l'adresse : https://hal.science/cel-01660629.
MORGAN, Malcolm, YOUNG, Marcus, LOVELACE, Robin et HAMA, Layik, 2019. OpenTripPlanner for R. In : Journal of Open Source Software [en ligne]. décembre 2019. Vol. 4, n° 44, pp. 1926. DOI 10.21105/joss.01926. Disponible à l'adresse : https://doi.org/10.21105/joss.01926.
PEREIRA, Rafael H. M., SARAIVA, Marcus, HERSZENHUT, Daniel, BRAGA, Carlos Kaue Vieira et CONWAY, Matthew Wigginton, 2021. r5r: Rapid Realistic Routing on Multimodal Transport Networks with R5 in R. In : Findings [en ligne]. mars 2021. DOI 10.32866/001c.21262. Disponible à l'adresse : http://dx.doi.org/10.32866/001c.21262.
PFERTNER, Maximilian, BÜTTNER, Benjamin et WULFHORST, Gebhard, 2023. An Open-Source Modelling Methodology for Multimodal and Intermodal Accessibility Analysis of Workplace Locations. In : Sustainability [en ligne]. janvier 2023. Vol. 15, n° 3, pp. 1947. DOI 10.3390/su15031947. Disponible à l'adresse : https://doi.org/10.3390/su15031947.

Annexes

Info session

setting value
version R version 4.4.0 (2024-04-24)
os Debian GNU/Linux 12 (bookworm)
system x86_64, linux-gnu
ui X11
language en
collate fr_FR.UTF-8
ctype fr_FR.UTF-8
tz Europe/Paris
date 2024-06-04
pandoc 3.1.11 @ /usr/lib/rstudio/resources/app/bin/quarto/bin/tools/x86_64/ (via rmarkdown)
package ondiskversion source
dplyr 1.1.4 CRAN (R 4.4.0)
DT 0.33 CRAN (R 4.4.0)
ggplot2 3.5.1 CRAN (R 4.4.0)
ggrepel 0.9.5 CRAN (R 4.4.0)
ggspatial 1.1.9 CRAN (R 4.4.0)
leaflet 2.2.2 CRAN (R 4.4.0)
mapsf 0.10.1 CRAN (R 4.4.0)
opentripplanner 0.5.2 CRAN (R 4.4.0)
prettymapr 0.2.5 CRAN (R 4.4.0)
reshape2 1.4.4 CRAN (R 4.4.0)
sf 1.0.16 CRAN (R 4.4.0)
stringr 1.5.1 CRAN (R 4.4.0)
tidyr 1.3.1 CRAN (R 4.4.0)
units 0.8.5 CRAN (R 4.4.0)

Citation

Ullès J, Le Texier M (2024). “Analyse d’accessibilité multimodale.”, doi:10.48645/5qht-d313 https://doi.org/10.48645/5qht-d313,, https://rzine.fr/articles_rzine/20240529_ulles_letexier_otp/.

BibTex :

@Misc{,
  title = {Analyse d’accessibilité multimodale},
  subtitle = {Avec R et OpenTripPlanner},
  author = {Jean-Clément Ullès and Marion {Le Texier}},
  doi = {10.48645/5qht-d313},
  url = {https://rzine.fr/articles_rzine/20240529_ulles_letexier_otp/},
  keywords = {FOS: Other social sciences},
  language = {fr},
  publisher = {FR2007 CIST},
  year = {2024},
  copyright = {Creative Commons Attribution Share Alike 4.0 International},
}



licensebuttons cc