From b3ffe9beb27ef1c933a39bf0f219b013392892e0 Mon Sep 17 00:00:00 2001 From: Said Turksever Date: Fri, 13 Nov 2026 15:38:18 +0000 Subject: [PATCH] feat(transit): add 112 French networks from transport.data.gouv.fr Each row is the keyless GTFS-RT vehicle feed of a national access point record whose licence field is Licence Ouverte 2.0 or ODbL, seen live on 2026-11-13. Rows live in transitFeedsFrance.js; line names for feeds that publish internal route keys come from each record's routes.txt. Networks already registered through the operator's own endpoint are not added again. --- DATA_SOURCES.md | 114 +- scripts/format-scope.json | 3 + scripts/package-boundaries.json | 20 + src/data/transitFeeds.js | 57 + src/data/transitFeeds.test.mjs | 5 + src/data/transitFeedsFrance.js | 2021 ++++++++++++++++++++++++++ src/data/transitFeedsFrance.test.mjs | 201 +++ src/data/transitRouteNamesFrance.js | 1402 ++++++++++++++++++ 8 files changed, 3822 insertions(+), 1 deletion(-) create mode 100644 src/data/transitFeedsFrance.js create mode 100644 src/data/transitFeedsFrance.test.mjs create mode 100644 src/data/transitRouteNamesFrance.js diff --git a/DATA_SOURCES.md b/DATA_SOURCES.md index b1f44764c..013314c86 100644 --- a/DATA_SOURCES.md +++ b/DATA_SOURCES.md @@ -175,6 +175,118 @@ How to read this: | **Toulon Mistral** (feed-rdtpm-toulon.ratpdev.com) | Transit layer live vehicles, Toulon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-de-transport-urbain-de-la-metropole-toulon-provence-mediterranee). Dataset record’s licence field quoted. Speeds are km/h: readings ran 3.5 times the measured movement on 2026-11-07, so the proxy converts them. Route ids are zero-padded. | Métropole Toulon Provence Méditerranée (Mistral) | | **Le Havre LiA** (opendata.transports-lia.fr) | Transit layer live vehicles, Le Havre, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-de-transport-en-commun-lia-gtfs-siri). The network publishes under ODbL (dataset record quoted); and this is its own GTFS-RT vehicle feed from that record; route ids carry the network prefix `8-`. The layer draws positions and redistributes no database. Lines A and B are the tramway. | Le Havre Seine Métropole (LiA) | | **Tours Fil Bleu** (data.filbleu.fr) | Transit layer live vehicles, Tours, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/fil-bleu-syndicat-des-mobilites-gtfs-gtfs-rt). Dataset record’s licence field quoted. | Fil Bleu – Syndicat des Mobilités de Touraine | +| **Agde Cap’Bus** (zenbus.net) | Transit layer live vehicles, Agde, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-capbus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (6 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Cap’Bus – Communauté d’agglomération Hérault-Méditerranée | +| **Agen Tempo** (api.pysae.com) | Transit layer live vehicles, Agen, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/agen-gtfs-urbain). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (33 vehicles on 2026-11-13). | Tempo Bus – Agglomération d’Agen | +| **Alès Alesy** (alesy.plateforme-2cloud.com) | Transit layer live vehicles, Alès, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-des-lignes-urbaines-et-interurbaines-sur-le-reseau-alesy-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (62 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | Alesy – Syndicat mixte transports publics du bassin d’Alès | +| **Aléop coaches** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Pays de la Loire, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/arrets-horaires-et-circuits-des-lignes-de-transports-aleop-1). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (103 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Route `YEU` is the Île d’Yeu boat. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Aléop - Pays de la Loire – Pays de la Loire | +| **Amiens Ametis** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Amiens, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/ametis). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (95 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Ametis – Communauté d’agglomération Amiens Métropole | +| **Angers Irigo** (ara-api.enroute.mobi) | Transit layer live vehicles, Angers, France | ODbL (https://transport.data.gouv.fr/datasets/angers-loire-metropole-reseau-irigo-gtfs-gtfs-rt-siri). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (214 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Lines A, B and C are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Irigo – Communauté urbaine Angers Loire Métropole | +| **Arles Envia** (accm.plateforme-2cloud.com) | Transit layer live vehicles, Arles, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-statiques-et-temps-reels-reseau-envia). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (33 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Envia – Communauté d’agglomération d’Arles-Crau-Camargue-Montagnette | +| **Arras Artis** (api.pysae.com) | Transit layer live vehicles, Arras, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-de-transport-artis). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Artis – Communauté urbaine d’Arras | +| **Aubenas Tout’enbus** (gtfs-rt.infra-hubup.fr) | Transit layer live vehicles, Aubenas, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Tout’enbus – Région Auvergne-Rhône-Alpes (CC du Bassin d’Aubenas) | +| **Auch Alliance** (zenbus.net) | Transit layer live vehicles, Auch, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-reseau-alliance-bus-grand-auch). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Alliance – Communauté d’agglomération Grand Auch Coeur de Gascogne | +| **Auray Glazgo** (api.pysae.com) | Transit layer live vehicles, Auray, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-glazgo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (4 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Glazgo – Communauté de communes Auray Quiberon Terre Atlantique | +| **Avignon Orizo** (api.maas-fr.cityway.fr) | Transit layer live vehicles, Avignon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-orizo-grand-avignon). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (90 vehicles on 2026-11-13). Line T1 is the tramway. | Orizo – Communauté d’agglomération du Grand Avignon (COGA) | +| **Barentin MOCA** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Barentin, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/moca-communaute-de-communes-caux-austreberthe). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. | MOCA – Communauté de communes Caux - Austreberthe | +| **Beaune Côte&Bus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Beaune, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/offre-de-transport-de-la-c-a-beaune-cote-sud-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Côte&Bus – Communauté d’agglomération Beaune, Côte et Sud - Communauté Beaune-Chagny-Nolay | +| **Beauvais Corolis** (api.oisemob.cityway.fr) | Transit layer live vehicles, Beauvais, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-corolis-urbain-communaute-dagglomeration-du-beauvaisis). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (23 vehicles on 2026-11-13). | Corolis – Communauté d’agglomération du Beauvaisis | +| **Beauvaisis Corolis interurbain** (api.oisemob.cityway.fr) | Transit layer live vehicles, Beauvais, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-corolis-interurbain-communaute-dagglomeration-du-beauvaisis). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (13 vehicles on 2026-11-13). | Corolis – Communauté d’agglomération du Beauvaisis | +| **Bernay l’Bus** (zenbus.net) | Transit layer live vehicles, Bernay, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-transport-urbain-de-bernay). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | l’Bus – Communauté de communes Intercom Bernay Terres de Normandie | +| **Blois Azalys** (app.mecatran.com) | Transit layer live vehicles, Blois, France | ODbL (https://transport.data.gouv.fr/datasets/agglopolys-offre-theorique-mobilite-reseau-urbain-azalys-de-blois). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (55 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Speeds are km/h: readings ran 6.63 times the measured movement on 2026-11-13, so the proxy converts them. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Azalys – Communauté d’agglomération de Blois "Agglopolys" | +| **Bourg-en-Bresse Rubis** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Bourg-en-Bresse, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/rubis-grand-bourg-mobilites-offre-de-transport-theorique-et-temps-reel). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (34 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 4.99 times the measured movement on 2026-11-13, so the proxy converts them. The feed carries no route ids. | Rubis – Communauté d’agglomération du Bassin de Bourg-en-Bresse | +| **Bourgoin-Jallieu Ruban** (app.pysae.com) | Transit layer live vehicles, Bourgoin-Jallieu, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-ruban). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (28 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | Ruban – Communauté d’agglomération Porte de l’Isère (CAPI) | +| **Brest Bibus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Brest, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-des-bus-et-tramways-circulant-sur-le-territoire-de-brest-metropole). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (113 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Lines A and B are the tramway; line C is the cable car, which has no mode of its own here and draws as a bus. Vehicles carry no timestamp of their own, so the feed header’s is used. | Bibus – Brest Métropole | +| **Béziers beeMob** (feed-beemob-beziers.ratpdev.com) | Transit layer live vehicles, Béziers, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-de-transports-beemob-beziers-mediterranee). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (40 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | beeMob – Communauté d’agglomération de Béziers-Méditerranée | +| **Cannes Palm Bus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Cannes, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-gtfs-gtfs-rt-du-reseau-palmbus-cannes-pays-de-lerins). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (86 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Palmbus – Communauté d’agglomération Cannes Pays de Lérins | +| **Carcassonne RTCA** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Carcassonne, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-rtca). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (23 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | RTCA – Communauté d’agglomération Carcassonne Agglo | +| **Caux Seine Rezo’Bus** (app.pysae.com) | Transit layer live vehicles, Lillebonne, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-caux-seine-mobilite-rezobus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | REZO’BUS – Communauté d’agglomération Caux Seine Agglo | +| **Cavaillon C’mon bus** (app.pysae.com) | Transit layer live vehicles, Cavaillon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-reseau-cmonbus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (11 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Cmonbus – Communauté d’agglomération Luberon Monts de Vaucluse | +| **Cayenne Agglo’bus** (cacl.plateforme-2cloud.com) | Transit layer live vehicles, Cayenne, French Guiana | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/agence-agglobus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (44 vehicles on 2026-11-13). | Agglobus – Communauté d’agglomération du Centre Littoral | +| **Chamonix buses** (api.pysae.com) | Transit layer live vehicles, Chamonix-Mont-Blanc, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-reseau-chamonix-mobilite). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (11 vehicles on 2026-11-13). | Communauté de communes de la Vallée de Chamonix-Mont-Blanc | +| **Chantilly Aire’MOB** (api.oisemob.cityway.fr) | Transit layer live vehicles, Chantilly, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-airemob-communaute-de-communes-de-laire-cantilienne). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). | Aire’MOB – Communauté de communes de l’Aire Cantilienne | +| **Cherbourg Cap Cotentin** (app.pysae.com) | Transit layer live vehicles, Cherbourg-en-Cotentin, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-en-temps-reel-des-bus-et-autocars-circulant-sur-le-reseau-cap-cotentin). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (53 vehicles on 2026-11-13). | Cap Cotentin – Communauté d’agglomération du Cotentin | +| **Chinon CVL Mobilité** (cvlmobilite.plateforme-2cloud.com) | Transit layer live vehicles, Chinon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-cvlmobilite-plan-de-transport-theorique-ligne-a-format-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). | CVL Mobilité – Communauté de communes Chinon, Vienne et Loire | +| **Clermont (Oise) Le Bus** (api.oisemob.cityway.fr) | Transit layer live vehicles, Clermont, Oise, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-le-bus-communaute-de-communes-du-clermontois). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (4 vehicles on 2026-11-13). | Le Bus – Communauté de communes du Clermontois | +| **Compiègne TIC** (api.oisemob.cityway.fr) | Transit layer live vehicles, Compiègne, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-tic-urbain-communaute-dagglomeration-de-la-region-de-compiegne-et-de-la-basse-automne). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (19 vehicles on 2026-11-13). | TIC – Communauté d’agglomération de la Région de Compiègne et de la Basse Automne | +| **Compiègne TIC interurbain** (api.oisemob.cityway.fr) | Transit layer live vehicles, Compiègne, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-tic-interurbain-communaute-dagglomeration-de-la-region-de-compiegne-et-de-la-basse-automne). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (21 vehicles on 2026-11-13). | TIC – Communauté d’agglomération de la Région de Compiègne et de la Basse Automne | +| **Cosne-sur-Loire Papillon** (api.pysae.com) | Transit layer live vehicles, Cosne-Cours-sur-Loire, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-du-reseau-de-bus-papillon-a-cosne-cours-sur-loire). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Papillon – Communauté de communes Coeur de Loire | +| **Creil AXO** (api.oisemob.cityway.fr) | Transit layer live vehicles, Creil, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-axo-communaute-dagglomeration-creil-sud-oise). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (28 vehicles on 2026-11-13). | AXO – Communauté d’agglomération Creil Sud Oise | +| **Crépy-en-Valois Mobi** (api.oisemob.cityway.fr) | Transit layer live vehicles, Crépy-en-Valois, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-mobi-communaute-de-communes-du-pays-de-valois). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). | Mobi – Communauté de communes du Pays de Valois | +| **Cœur du Var Le TACO** (api.pysae.com) | Transit layer live vehicles, Le Luc, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/le-taco-1). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Le TACO – Région Provence-Alpes-Côte d’Azur (CC Coeur du Var) | +| **Dieppe DeepMob** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Dieppe, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/deepmob-dieppe-maritime). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (12 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | DeepMob – Communauté d’agglomération de la Région Dieppoise | +| **Dijon Divia** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Dijon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-diviamobilites). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (149 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 4.42 times the measured movement on 2026-11-13, so the proxy converts them. Lines T1 and T2 are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | DiviaMobilités – Dijon Métropole | +| **Dole Grandole Mobilités** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Dole, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-de-transport-du-grand-dole). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (18 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Grandole Mobilités – Communauté d’agglomération du Grand Dole | +| **Draguignan TEDbus** (pysae.com) | Transit layer live vehicles, Draguignan, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-tedbus-lignes-regulieres-dracenie-provence-verdon-agglomeration). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (15 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | TEDbus – Dracénie Provence Verdon Agglomération | +| **Figeac Le Bus** (zenbus.net) | Transit layer live vehicles, Figeac, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-du-reseau-figeac-le-bus-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (3 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Figeac - Le Bus – Région Occitanie (CC Grand-Figeac) | +| **Flers Némus** (flers-agglo.plateforme-2cloud.com) | Transit layer live vehicles, Flers, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-du-reseau-urbain-de-flers-agglo-au-format-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Nemus – Communauté d’agglomération Flers Agglo | +| **Fougères SURF** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Fougères, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-surf). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (8 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | SURF – Communauté d’agglomération Fougères Agglomération | +| **Fréjus–Saint-Raphaël Le Bus** (api.pysae.com) | Transit layer live vehicles, Fréjus, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-transport-urbain-le-bus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (31 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Le Bus – Estérel Côte d’Azur Agglomération | +| **Gap L’Agglo en Bus** (gtfs-rt.infra-hubup.fr) | Transit layer live vehicles, Gap, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reels-du-reseau-de-transports-lagglo-en-bus-communaute-dagglomeration-gap-tallard-durance). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (11 vehicles on 2026-11-13). | L’Agglo en Bus – Communauté d’agglomération Gap-Tallard-Durance | +| **Guingamp-Paimpol Mobilité** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Guingamp, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-scolaire-guingamp-paimpol-mobilite). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (13 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. | Guingamp-Paimpol Mobilité – Communauté d’agglomération Guingamp-Paimpol Agglomération de l’Armor à l’Argoat | +| **Guérande Lila Presqu’île** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Guérande, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-scolaire-lila). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (20 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Lila Presqu’ile – Communauté d’agglomération de la Presqu’île de Guérande Atlantique (CAP ATLANTIQUE) | +| **Haguenau Ritmo** (zenbus.net) | Transit layer live vehicles, Haguenau, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-ritmo-haguenau-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (20 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Ritmo – Communauté d’agglomération de Haguenau | +| **Hazebrouck Hop Bus** (gtfs-rt.infra-hubup.fr) | Transit layer live vehicles, Hazebrouck, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/hop-bus-le-reseau-100-gratuit-du-coeur-de-flandre). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (8 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Hop Bus – Communauté d’agglomération Coeur de Flandre | +| **Honfleur Hobus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Honfleur, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/hobus-honfleur). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Hobus – Communauté de communes du Pays de Honfleur-Beuzeville | +| **Issoire Cherpa** (zenbus.net) | Transit layer live vehicles, Issoire, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/cherpa-horaires-lignes-regulieres). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Cherpa – Agglo Pays d’Issoire | +| **Lavaur Lisatis** (zenbus.net) | Transit layer live vehicles, Lavaur, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-de-transport-urbain-lisatis). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (3 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | LISATIS – Région Occitanie (CC Tarn-Agout) | +| **Le Mans SETRAM** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Le Mans, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-du-reseau-des-transports-bus-et-tramway-setram-circulant-sur-le-territoire-le-mans-metropole). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (102 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 3.67 times the measured movement on 2026-11-13, so the proxy converts them. Lines T1 and T2 are the tramway. | SETRAM – Communauté urbaine Le Mans Métropole | +| **Le Tampon CarSud** (zenbus.net) | Transit layer live vehicles, Le Tampon, La Réunion, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/referentiel-topologique-reseau-carsud). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (22 vehicles on 2026-11-13). | CarSud – Communauté d’agglomération du Sud | +| **Liancourt Le Libellule** (api.oisemob.cityway.fr) | Transit layer live vehicles, Liancourt, France | ODbL (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-le-libellule-communaute-du-liancourtois-la-vallee-doree). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | Libellule – Communauté de communes du Liancourtois | +| **Lisieux Astrobus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Lisieux, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/astrobus-lisieux-normandie). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (6 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Astrobus – Communauté d’agglomération Lisieux Normandie | +| **Longwy TGL** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Longwy, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-du-reseau-tgl). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (21 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | TGL – Syndicat mixte intercommunal des transports de l’agglomération de Longwy | +| **Lunel Olé** (ole.plateforme-2cloud.com) | Transit layer live vehicles, Lunel, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/offre-de-transport-du-reseau-ole-lunel-agglo-gtfs-et-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). | Olé – Communauté d’agglomération Lunel Agglo | +| **Lunéville Lunéo** (pysae.com) | Transit layer live vehicles, Lunéville, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-lulu-en-bus-ex-luneo-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Lunéo – PETR du Pays du Lunévillois | +| **Martinique Centre (CACEM)** (ext.saemartinique.ovh:4435) | Transit layer live vehicles, Fort-de-France, Martinique, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-urbain-de-la-zone-centre). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (28 vehicles on 2026-11-13). | de la zone Centre (CACEM) – Martinique Transport | +| **Martinique Sud (CAESM)** (api.pysae.com) | Transit layer live vehicles, Southern Martinique, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-urbain-de-la-zone-sud). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (42 vehicles on 2026-11-13). Routes 4001 to 4004 are the Vedettes boats. | de la zone Sud (CAESM) – Martinique Transport | +| **Mayenne May’bus** (zenbus.net) | Transit layer live vehicles, Mayenne, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-maybus-mayenne-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | May’bus – Communauté de communes Mayenne Communauté | +| **Mende TUM** (zenbus.net) | Transit layer live vehicles, Mende, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-max-mende-mobilites-bus-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | TUM – Région Occitanie (CC Coeur de Lozère) | +| **Menton Zest** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Menton, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-reseau-zest). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (22 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Zest – Communauté d’agglomération de la Riviera Française | +| **Metz Le Met’** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Metz, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/fichiers-gtfs-eurometropole-de-metz). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (140 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Le Met’ – Metz Métropole | +| **Montluçon Maelis** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Montluçon, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-reel-du-reseau-maelis-montlucon-communaute-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (15 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 3.3 times the measured movement on 2026-11-13, so the proxy converts them. The feed carries no route ids. | Maelis – Communauté d’agglomération Montluçon Communauté | +| **Montélimar Montélibus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Montélimar, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-montelibus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (20 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Montelibus – Communauté d’agglomération Montélimar Agglomération | +| **Moulins Aléo** (api.pysae.com) | Transit layer live vehicles, Moulins, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-du-reseau-de-transport-aleo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (16 vehicles on 2026-11-13). | Aléo – Communauté d’agglomération Moulins Communauté | +| **Narbonne Citibus** (feed-citibus-narbonne.ratpdev.com) | Transit layer live vehicles, Narbonne, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/offre-de-transport-communaute-dagglomeration-le-grand-narbonne). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (54 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Citibus – Communauté d’agglomération Le Grand Narbonne | +| **Nice Lignes d’Azur** (ara-api.enroute.mobi) | Transit layer live vehicles, Nice, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-statiques-et-dynamiques-du-reseau-de-transport-lignes-dazur). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (241 vehicles on 2026-11-13). Lines L1, L2 and L3 are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Lignes d’Azur – Métropole Nice Côte d’Azur | +| **Nogent-le-Rotrou Nosbus** (api.pysae.com) | Transit layer live vehicles, Nogent-le-Rotrou, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-nosbus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Nosbus – Région Centre-Val de Loire (CC du Perche) | +| **Noyon Lib’Bus** (api.oisemob.cityway.fr) | Transit layer live vehicles, Noyon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-libbus-communaute-de-communes-du-pays-noyonnais). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). | Lib’Bus – Communauté de communes du Pays Noyonnais | +| **Oloron-Sainte-Marie La Navette** (zenbus.net) | Transit layer live vehicles, Oloron-Sainte-Marie, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-oloron-sainte-marie-la-navette-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | La Navette Oloron Sainte-Marie – Communauté de communes du Haut Béarn | +| **Orange TCVO** (tcvo.2cloud.app) | Transit layer live vehicles, Orange, France | ODbL (https://transport.data.gouv.fr/datasets/transport-en-commun-de-la-ville-dorange-ccpop-1). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | TCVO – Pays d’Orange en Provence | +| **Pays de l’Or Transp’Or** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Mauguio, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/offre-transport-en-commun-du-reseau-transpor-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (6 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Transp’Or – Communauté d’agglomération du Pays de l’Or | +| **Pays des Écrins buses** (app.pysae.com) | Transit layer live vehicles, L’Argentière-la-Bessée, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/transports-en-commun-pays-des-ecrins). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). | Communauté de communes du Pays des Ecrins | +| **Perpignan Sankéo** (zenbus.net) | Transit layer live vehicles, Perpignan, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-sankeo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (50 vehicles on 2026-11-13). | Sankéo – Communauté urbaine Perpignan Méditerranée Métropole | +| **Pont-Sainte-Maxence TOHM** (api.oisemob.cityway.fr) | Transit layer live vehicles, Pont-Sainte-Maxence, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-theoriques-et-temps-reel-du-reseau-tohm-communaute-de-communes-des-pays-doise-et-dhalatte). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). | TOHM – Communauté de communes des Pays d’Oise et d’Halatte | +| **Pontivy PondiBUS** (pysae.com) | Transit layer live vehicles, Pontivy, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-pondibus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | PondiBUS – Communauté de communes Pontivy Communauté | +| **Porto-Vecchio A Citadina** (pysae.com) | Transit layer live vehicles, Porto-Vecchio, Corsica, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-a-citadina-porto-vecchio-horaires-theoriques-et-temps-reel). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | A Citadina – Communauté de communes du Sud Corse | +| **Quimper QUB** (feed-qub-quimper.ratpdev.com) | Transit layer live vehicles, Quimper, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-du-reseau-de-transports-qub-transports-urbains-de-quimper-bretagne-occidentale). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (49 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Qub – Communauté d’agglomération Quimper Bretagne Occidentale | +| **Riom RLV Mobilités** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Riom, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-rlv-mobilites). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | RLV Mobilités – Communauté d’agglomération Riom Limagne et Volcans | +| **Rosny-sous-Bois Titus** (zenbus.net) | Transit layer live vehicles, Rosny-sous-Bois, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-titus-rosny-sous-bois-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Titus – Île-de-France Mobilités | +| **Rumilly J’ybus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Rumilly, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/offre-de-transports-jybus-a-rumilly). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (6 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Jybus – Communauté de communes Rumilly Terre de Savoie | +| **Rémi coaches** (api.okina.fr) | Transit layer live vehicles, Centre-Val de Loire, France | ODbL (https://transport.data.gouv.fr/datasets/remi-offre-theorique-mobilite-reseau-interurbain-regional). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (100 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Rémi – Centre-Val de Loire | +| **Réunion Car Jaune** (pysae.com) | Transit layer live vehicles, La Réunion, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-au-format-gtfs-et-horaires-temps-reel-au-format-gtfs-rt-du-reseau-car-jaune-a-la-reunion). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (59 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Car Jaune – La Réunion | +| **Sablé-sur-Sarthe Réso** (gtfs-rt.infra-hubup.fr) | Transit layer live vehicles, Sablé-sur-Sarthe, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-reso). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Réso – Communauté de communes du Pays Sabolien | +| **Saint-Brevin Brévibus** (pysae.com) | Transit layer live vehicles, Saint-Brevin-les-Pins, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-brevibus). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Brévibus – Communauté de communes du Sud Estuaire | +| **Saint-Malo MAT** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Saint-Malo, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-urbain-mat). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (30 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | MAT – Communauté d’agglomération du Pays de Saint Malo Agglomération | +| **Saint-Omer Mouvéo** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Saint-Omer, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/transports-en-commun-audomarois-1). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (32 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Mouvéo – Communauté d’agglomération du Pays de Saint-Omer | +| **Saint-Pierre Alternéo** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Saint-Pierre, La Réunion, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/donnees-du-reseau-alterneo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (17 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Alternéo – Communauté d’agglomération CIVIS (Communauté Intercommunale des Villes Solidaires) | +| **Saint-Sulpice-la-Pointe buses** (zenbus.net) | Transit layer live vehicles, Saint-Sulpice-la-Pointe, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-de-transport-urbain-de-la-ville-de-st-sulpice-la-pointe). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (2 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | ST SULPICE LA POINTE – Région Occitanie (CC Tarn-Agout) | +| **Saintes Buss** (zenbus.net) | Transit layer live vehicles, Saintes, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-buss-saintes-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (19 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Buss – Communauté d’agglomération de Saintes | +| **Sarlat Bus** (zenbus.net) | Transit layer live vehicles, Sarlat-la-Canéda, France | ODbL (https://transport.data.gouv.fr/datasets/horaires-theoriques-et-temps-reel-du-reseau-sarlatbus-gtfs-gtfs-rt). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (1 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Sarlat Bus – Communauté de communes Sarlat-Périgord Noir | +| **Sarthe Illygo** (pysae.com) | Transit layer live vehicles, Sarthe, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-illygo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (5 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Illygo – Pôle Métropolitain Mobilités Le Mans – Sarthe | +| **Sète Agglopôle Mobilité** (sete.ceccli.com) | Transit layer live vehicles, Sète, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-bus-sete-agglopole-mobilite). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (48 vehicles on 2026-11-13). | Sète Agglopole Mobilité – Communauté d’agglomération Sète Agglopôle Méditerranée | +| **Tarbes–Lourdes TLP Mobilités** (zenbus.net) | Transit layer live vehicles, Tarbes, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-du-reseau-tarbes-lourdes-tlp-mobilites-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (24 vehicles on 2026-11-13). | TLP – Communauté d’agglomération Tarbes-Lourdes-Pyrénées | +| **Troyes TCAT** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Troyes, France | ODbL (https://transport.data.gouv.fr/datasets/donnees-tcat-troyes-champagne-metropole-1). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (52 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. | TCAT – Communauté d’agglomération Troyes Champagne Métropole | +| **Valence–Romans Citéa** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Valence, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/ce-jeu-de-donnees-contient-la-liste-des-arrets-des-horaires-et-des-parcours-theoriques-du-reseau-de-transport-urbain-et-interurbain-de-vrm). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (130 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Citéa – Valence-Romans déplacements | +| **Valenciennes Transvilles** (ara-api.enroute.mobi) | Transit layer live vehicles, Valenciennes, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/simouv-horaires-theoriques-format-gtfs-et-temps-reel-format-siri-transvilles-valenciennes). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (87 vehicles on 2026-11-13). Lines T1 and T2 (route ids 190 and 191) are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Transvilles – Syndicat intercommunal de mobilité et d’organisation urbaine du Valenciennois | +| **Vannes Kicéo** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Vannes, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/reseau-urbain-kiceo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (63 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Kicéo – Communauté d’agglomération Golfe du Morbihan - Vannes Agglomération | +| **Vendôme Move** (pysae.com) | Transit layer live vehicles, Vendôme, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/gtfs-move-vendome). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Move – Communauté d’agglomération Territoires Vendômois | +| **Vernon SNgo!** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Vernon, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/sngo-vernon-les-andelys-seine-normandie-agglo). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | SNgo! – Communauté d’agglomération Seine Normandie Agglomération | +| **Vesoul Moova** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Vesoul, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/horaires-theoriques-du-reseau-moova-communaute-dagglomeration-de-vesoul-gtfs). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (7 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | moova – Communauté d’agglomération de Vesoul | +| **Vichy Mobivie** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Vichy, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/mobivie-reseau-urbain-vichy-communaute). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (11 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. | Mobivie – Communauté d’agglomération Vichy Communauté | +| **Vienne L’va** (feed-vienne-mobi.ratpdev.com) | Transit layer live vehicles, Vienne, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-lva). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (44 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | L’va – Communauté d’agglomération Vienne Condrieu | +| **Vierzon Le Vib’** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Vierzon, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-urbain-le-vib). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (4 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Le Vib’ – Région Centre-Val de Loire (CC Vierzon-Sologne-Berry) | +| **Yvetot Vikibus** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Yvetot, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/vikibus-yvetot-normandie). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (3 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Vikibus – Communauté de communes Yvetot Normandie | +| **liO Occitanie coaches** (lio.2cloud.app) | Transit layer live vehicles, Occitanie, France | ODbL (https://transport.data.gouv.fr/datasets/reseau-lio-occitanie). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (243 vehicles on 2026-11-13). ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database. | liO Occitanie – Occitanie | +| **Évreux Transurbain** (proxy.transport.data.gouv.fr) | Transit layer live vehicles, Évreux, France | Licence Ouverte 2.0 (https://transport.data.gouv.fr/datasets/transurbain-evreux-portes-de-normandie). Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (24 vehicles on 2026-11-13). Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS. | Transurbain – Communauté d’agglomération Evreux Portes de Normandie | | **Radio Browser** | Geolocated internet-radio station directory and station-level tags | Public-domain directory data under PDDL 1.0; individual broadcaster stream terms apply | "Radio Browser" plus a link to the selected broadcaster | | **Re:Earth Terrain** (Mapterhorn) | Terrain (keyless globe stacks — OSM etc. — + `/api/terrain/heights` ellipsoidal-height lookups) | Terrain mesh: CC BY 4.0; geoid: EGM2008 (NGA, public domain) | "Terrain (keyless globe stacks): Re:Earth Terrain / Mapterhorn (CC BY 4.0) / EGM2008 (NGA)" | | **OpenSeaMap** (`tiles.openseamap.org`) | Sea Marks layer: raster overlay of buoys, lighthouses and ports (#10) | Chart tiles CC BY-SA 2.0; data ODbL 1.0 via OpenStreetMap | "Sea marks © OpenSeaMap contributors (CC BY-SA 2.0) · Data © OpenStreetMap contributors (ODbL)" | @@ -268,7 +380,7 @@ The optional Local ADS-B layer and the broadcast-FM receiver use radio you recei - **Houston TranStar cameras.** Keyless. The camera list is the public `traffic.houstontranstar.org/data/layers/cctvSnapshots_json.js` catalog (~1,000 cameras across Greater Houston); frames are stills at `www.houstontranstar.org/snapshots/cctv/.jpg`, synthesized from a validated numeric id rather than taken from the payload, and only that host is registered. Cameras whose catalog id is `0` (a different, non-snapshot class) and cameras outside Greater Houston are dropped. TranStar's `dir` field is the side of the freeway the camera sits on, not the way it faces: TranStar's own map shows it as "Traffic closest to the camera is traveling …" (South → eastbound, North → westbound, West → southbound, East → northbound). That travel direction (90°, 270°, 180°, 0°) seeds the heading as a low-confidence prior with the low-confidence pose; `None` or an unknown value uses the shared id-hash fallback at low confidence. Headings are corrected with the in-app calibration gizmo. Off by default. TranStar's [Content Usage terms](https://traffic.houstontranstar.org/disclaimer.aspx) prohibit copying or distributing the site's information, and external framing or linking to specific images, without TxDOT's prior written permission; its FAQ grants permission-free use only for the RSS feeds. This project does not hold that permission for downstream installations, so the pack loads only when an operator who holds TxDOT's written permission sets `CCTV_HOUSTON_ENABLED=1`; with the flag unset, empty or any other value, the server makes no request to TranStar. Once enabled, it loads the 250 cameras nearest downtown, Uptown, the Medical Center, IAH and Galveston; `CCTV_HOUSTON_MAX_SOURCES` (8–400) changes the cap. Snapshots update about every 3 minutes, which is also the ambient still cadence. Frames are fetched from TranStar and relayed to the browser on demand, buffered only transiently per request, sent with `Cache-Control: no-store`, and not archived. - **Autostrada del Brennero (A22) webcams.** Keyless. The camera list is one request to the Open Data Hub tourism API (`tourism.api.opendatahub.com/v1/WebcamInfo?source=a22`), run by NOI Techpark in South Tyrol, which republishes Autobrennero's own list of 13 motorway webcams with coordinates and kilometre markers. Frames are the operator's stills on `www.autobrennero.it/WebCamImg/km.jpg`, refreshed about once a minute, and only the exact image for each camera's kilometre is registered. The feed names no bearing, so headings use the id-hash fallback at low confidence. The road climbs from about 20 m in the Po valley to about 1,370 m at the Brenner Pass, so each camera's ground elevation comes from a per-kilometre table sampled from the Copernicus GLO-90 DEM (via Open-Meteo's elevation API); a camera not in the table uses a 200 m prior. The Open Data Hub tags every record CC0 (`ClosedData: false`, Autobrennero as licence holder), but that tag is the aggregator's metadata and Autobrennero publishes no terms of its own for the images, so they are credited to the operator as a courtesy and are not redistributed. Frames are fetched at request time and never stored. Disable with `CCTV_A22_ENABLED=0`. - **Camera frame content.** Public camera frames can contain people, vehicles and license plates, and the camera layer does nothing with them beyond putting them on the map. A successful upstream response is relayed as the provider served it: nothing in the camera pipeline blurs, redacts, enhances or restores it, and nothing there runs face, plate or object detection or otherwise recognises or classifies what is in the picture. (The ALPR layer described below maps where plate-reader cameras are, from locations contributors tagged in OpenStreetMap; it reads nothing from any camera frame.) When an upstream has no frame for a camera, what you see instead is a Google Street View still or the app's own placeholder card; the panel's source badge names which of them you are looking at, and a refresh that fails after a frame has arrived leaves the last good picture on screen rather than replacing it. The browser resamples each frame for display: onto a fixed 1920x1080 surface for the camera's projection plane in the scene, and onto a 192x108 canvas for the small ambient card. That scaling is the only change made to the picture, and the only pixels the camera layer reads back are a 64x36 downscale hashed to tell whether the picture has changed. Live streams are piped through; the still path reads the image into memory to pass it on — TxDOT wraps its JPEG in a JSON envelope, which is decoded there — and no frame is written to disk (the disk cache, `.gev-cache/`, holds feed JSON for other layers and never camera imagery). Outside that pipeline, the voice assistant is the one feature that sends imagery anywhere: when it answers a question about what is in view it may attach a downscaled screenshot of the rendered scene — including any camera plane on screen — to the request it sends to the voice model. Frames are fetched only from server-registered camera URLs — a client cannot hand the proxy a URL (see [SECURITY.md](SECURITY.md)). What the app does stays inside the line README draws: no named-person search, no face recognition, no tracking individuals. -- **Transit (GTFS-Realtime).** The proxy requests only registered operator feeds, validates redirects, bounds responses, and uses conditional requests, short snapshot caching and failure backoff. All fifty-two registered feeds, covering forty-nine regions, are live-enabled; each can be disabled independently. Vehicles use delayed playback between reports, with report age and waiting states shown separately. Session-local selected trails are available for every feed. MBTA additionally retains up to 15 minutes of recently observed positions in the running proxy, so available history can survive a browser reload; server restart clears it. History reads never fetch upstream, and other feeds have no proxy retention. Nothing is archived to disk or collected in the background. Operator text credits are included as a courtesy and do not imply endorsement. Heights are aligned to work with Google 3D tiles; subway markers are shown at street level because these feeds do not supply depth. The TTC feed is BusTime, the surface system: it carries buses and streetcars and no subway at all. About four in ten of its vehicles report no route at all (unassigned or deadheading), and those are shown with the feed default rather than a route-established mode. It sends no ETag or Last-Modified, so its conditional requests fall through to the ordinary snapshot cache. Three Golden Horseshoe neighbours run alongside it — MiWay, Hamilton HSR and Durham Region Transit — each bus-only and each polled only near its own region. Durham Region Transit publishes no bearing on any vehicle, so those sprites take their heading from observed movement like any other feed that omits it. Guelph, Kingston and Barrie extend the same pattern eastward and north. Hamilton, Guelph, Burlington, YRT and Rio’s SMTR publish route ids that are not the number on the vehicle, so those entries carry a route-name table taken from each operator’s own published GTFS; any id missing from a table renders unchanged rather than blank. A label is never invented: YRT runs a route `9101` that appears nowhere in its published routes, so it stays as the feed sends it. Burlington’s is the one that cannot be shortcut: `351` is route 1 while `3510` is route 10, so the table is a real join rather than a trimmed prefix. GO Transit / Metrolinx is deliberately absent: its REALTIME feed requires an API key issued by manual review, which the keyless admission rule excludes (its static schedule download is keyless, but carries no vehicle positions). Barrie and YRT/Viva each invite acceptance of their licence through a form, and each licence also states that using the data is itself acceptance; this deployment relies on that clause, which is recorded in both entries. Grand River Transit in Waterloo is absent for a different reason: it answers, but its feed header was months stale and it publishes no per-vehicle timestamps, so it would render vehicles that look live and are not. Ottawa’s OC Transpo falls to the same keyless admission rule as GO Transit — its realtime feed requires an API key. London Transit and Thunder Bay Transit publish vehicle positions over plain HTTP with no HTTPS endpoint available. Every other Ontario system — Brampton, Oakville, Niagara, Milton, Windsor, Greater Sudbury, UP Express and a dozen smaller operators — publishes static GTFS schedules only, with no realtime vehicle positions to register. Between the nine included and these exclusions, every Ontario realtime vehicle-position feed catalogued in the Mobility Database is accounted for. +- **Transit (GTFS-Realtime).** The proxy requests only registered operator feeds, validates redirects, bounds responses, and uses conditional requests, short snapshot caching and failure backoff. All 164 registered feeds are live-enabled; each can be disabled independently. Of those, 112 are French networks taken from the national access point, transport.data.gouv.fr: each is the GTFS-RT vehicle feed of a dataset record whose licence field is Licence Ouverte 2.0 or ODbL, and each returned live vehicle positions on 2026-11-13. Their rows live in `src/data/transitFeedsFrance.js`, with line names for the feeds that publish internal route keys in `src/data/transitRouteNamesFrance.js`, copied from each record’s own GTFS `routes.txt`. A network that was already here through its operator’s own endpoint (Bordeaux, Montpellier, Rouen, Toulon, Le Havre, Tours) was not added a second time, and neither was the Atoumod aggregate, which repeats Normandy networks listed one by one. Feeds republished by bus-tracker.fr, feeds served over plain HTTP, and the three Fluo Grand Est feeds, whose positions ran seven to fifteen minutes old, are left out. Vehicles use delayed playback between reports, with report age and waiting states shown separately. Session-local selected trails are available for every feed. MBTA additionally retains up to 15 minutes of recently observed positions in the running proxy, so available history can survive a browser reload; server restart clears it. History reads never fetch upstream, and other feeds have no proxy retention. Nothing is archived to disk or collected in the background. Operator text credits are included as a courtesy and do not imply endorsement. Heights are aligned to work with Google 3D tiles; subway markers are shown at street level because these feeds do not supply depth. The TTC feed is BusTime, the surface system: it carries buses and streetcars and no subway at all. About four in ten of its vehicles report no route at all (unassigned or deadheading), and those are shown with the feed default rather than a route-established mode. It sends no ETag or Last-Modified, so its conditional requests fall through to the ordinary snapshot cache. Three Golden Horseshoe neighbours run alongside it — MiWay, Hamilton HSR and Durham Region Transit — each bus-only and each polled only near its own region. Durham Region Transit publishes no bearing on any vehicle, so those sprites take their heading from observed movement like any other feed that omits it. Guelph, Kingston and Barrie extend the same pattern eastward and north. Hamilton, Guelph, Burlington, YRT and Rio’s SMTR publish route ids that are not the number on the vehicle, so those entries carry a route-name table taken from each operator’s own published GTFS; any id missing from a table renders unchanged rather than blank. A label is never invented: YRT runs a route `9101` that appears nowhere in its published routes, so it stays as the feed sends it. Burlington’s is the one that cannot be shortcut: `351` is route 1 while `3510` is route 10, so the table is a real join rather than a trimmed prefix. GO Transit / Metrolinx is deliberately absent: its REALTIME feed requires an API key issued by manual review, which the keyless admission rule excludes (its static schedule download is keyless, but carries no vehicle positions). Barrie and YRT/Viva each invite acceptance of their licence through a form, and each licence also states that using the data is itself acceptance; this deployment relies on that clause, which is recorded in both entries. Grand River Transit in Waterloo is absent for a different reason: it answers, but its feed header was months stale and it publishes no per-vehicle timestamps, so it would render vehicles that look live and are not. Ottawa’s OC Transpo falls to the same keyless admission rule as GO Transit — its realtime feed requires an API key. London Transit and Thunder Bay Transit publish vehicle positions over plain HTTP with no HTTPS endpoint available. Every other Ontario system — Brampton, Oakville, Niagara, Milton, Windsor, Greater Sudbury, UP Express and a dozen smaller operators — publishes static GTFS schedules only, with no realtime vehicle positions to register. Between the nine included and these exclusions, every Ontario realtime vehicle-position feed catalogued in the Mobility Database is accounted for. - **Rail (GTFS-Realtime trains).** Each feed in `src/data/railFeeds.js` records its licence, its credit and the passage of its terms the decision rests on, and a feed is switched on only when those terms were read and cover this use, or, for those that publish no licence at all, on the basis its entry records. Such a basis is never a licence, and the credit does not present it as one: its label says there is no licence ("No licence published", "No licence stated") or names the publication the use rests on. Five enabled feeds publish no licence for their train positions. MARC is used on what its publication says (open publication with a no-endorsement disclaimer), and METRO light rail on the same basis as the Transit layer's Metro Transit feed. Tri-Rail and Charlotte's LYNX are used without a published licence by project owner decision of 2026-11-10. The fifth is Irish Rail, whose train positions publish no licence while its NTA timetable is CC BY 4.0. The registry lists 30 feeds, all switched on, and every one is keyless: its realtime feed and its static timetable are published for anyone to fetch, so no feed needs a key, an account or a registration, and the server holds no rail credential. No rail request carries a credential of the deployer's; one feed's published URL includes a fixed public value its publisher prints for everyone (Bordeaux's `apiKey`). `/api/rail/feeds` lists the offered feeds, `/api/rail/trains/` returns one feed's train snapshot, `/api/rail/updates/` its trip updates, and `/api/rail/paths/` and `/api/rail/schedule/` its path and schedule packs, when built. The proxy requests only registered feeds and reuses the Transit proxy's request policy (a short snapshot cache with single-flight refresh, serve-stale, admission limits, the failure cooldown ladder, redirects followed hop by hop only on the feed's own https origin, and an 8 MiB body cap unless a feed sets its own, at most 64 MiB). Many operators publish trains in the same feed as their buses, so each feed has a classifier: the proxy keeps only vehicles it recognises as trains, tags each with a category (`transit`, `local`, `regional`, `national` or `international`) and a mode (`rail`, `metro` or `tram`), and drops everything else server-side, counted in the snapshot as `dropped`; the browser never receives a bus. Every train shown is live, where its operator last reported it: 29 feeds publish live GPS positions (SJ Nord's through SIRI VM), and Irish Rail reports the station or timing point a train last passed. No train is placed or moved from a timetable; a feed whose live positions fail shows as unavailable, and its trains age out as any quiet train does. 22 feeds register trip updates, polled while one of their trains is selected and that feed's schedule pack is loaded. 28 feeds work out of the box. Packs are an opt-in extra for live trains: nothing is downloaded when the app starts, and `node scripts/build-rail-paths.mjs --feed ` (repeatable) or `--all` downloads a feed's static GTFS (29 of the 30 have one) and writes a path pack (the operator's track shapes) and a schedule pack (the stop times for a window of days) to `.cache/rail-paths/` (or to `RAIL_PATHS_DIR` when set, the directory the server then serves). With them, a live train is drawn on its operator's route line and its card names its stops, its next stop and, from the trip updates, their delays. Renfe long and medium distance and KTMB, whose vehicles name no route, are classified from their pack's routes, so `/api/rail/feeds` lists them only once that pack is built. Lines are the operator's own shapes as published: nothing is routed or redrawn from other map data, and no line is drawn where an operator publishes no shape. **Share-alike sources bind the deployer.** A pack built from a ShareAlike or ODbL source is a derived database of it, so a deployer who serves it publicly makes it subject to the same licence and must offer it under that licence with the attribution kept. That covers the Montpellier packs (ODbL). For the ODbL datasets of France's national access point, its special conditions also require that "Les bases de données ainsi repartagées doivent être publiées sur le Point d'accès national transport.data.gouv.fr, dans le format d'origine" ([Licence ODbL](https://doc.transport.data.gouv.fr/le-point-d-acces-national/cadre-juridique/conditions-dutilisation-des-donnees/licence-odbl), read 2026-11-10): a deployer who serves the Montpellier packs publicly takes on republishing that derived data there, as GTFS. The build downloads each static GTFS itself, over https and with no credential. A feed's `staticRouteTypes` lists the route types it keeps when its classifier accepts every route of a mode-specific feed. A frequency-based trip becomes one run per departure on its template's stops and run times. Packs are build output, not part of the repository, and cover a fixed window of days, so they are rebuilt before it ends, and more often where an operator republishes more often. A build in which any feed fails exits with code 2, the other feeds still built. A stop the GTFS gives no coordinates, which some feeds write as (0, 0), is left out of the run, so no line is ever drawn to it and no stop is placed by guesswork. Where several loaded feeds carry the same train, it is drawn once. Copies are matched by train number within 100 km, or, for a short line name (S1, RB 1, U6), on the same line in the same category within 15 km. A train number is three or more digits, or a shorter number after a long-distance product (EC, ECE, EN, ICE, NJ, RJ, RJX, TGV; not IC, where a short number names a line); a trip id that is an operator prefix and a number, or a headsign that is a bare number, is read too. The copy drawn is scored by its position source (live GPS, then station reports), then by its line (on its operator's shape, then without one), and stays drawn until another scores strictly higher. No train is ever placed in Ukraine, a safety rule: the build leaves out every run with a stop there, a stop whose id names Ukraine or whose position is inside its recognised borders (either is enough: an id naming another country does not keep a stop inside them), and the proxy drops every live position inside those borders (`src/data/railNoPositions.js`). Trains use the Transit layer's delayed playback between reports and move along their line in the path pack where there is one, else in straight lines. No train position is archived to disk, there is no proxy history, and nothing is collected in the background. Operator credits appear when that feed's trains are shown and do not imply endorsement. - **Radio country coverage (optional).** Set `RADIO_PRIORITY_COUNTRIES=IN,PL` in the local `.env` and restart to include stations beyond the global click charts. The operator may choose up to eight unique ISO alpha-2 country codes; no key is needed. Each country adds one bounded query (at most 200 rows) and reserves up to 40 unique, usable stations inside the existing 750-station catalog. Country seeds precede specialist-tag seeds and global popularity fill; unused slots return to the shared pool. This is a coverage preference, not an exhaustive national directory or a guarantee of stations in every city. Countries with no eligible HTTPS MP3/AAC geolocated stations remain empty. An unset list preserves global selection. Country requests share the directory cache and three-worker pool; failed country queries are reported in coverage without blocking a healthy global refresh. Failed/degraded refreshes back off for one minute, with at most three mirrors per query. diff --git a/scripts/format-scope.json b/scripts/format-scope.json index df01df506..b71f0ccfd 100644 --- a/scripts/format-scope.json +++ b/scripts/format-scope.json @@ -801,6 +801,9 @@ "server/providers/transitHistory.js", "server/providers/transitHistory.test.mjs", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitFeedsFrance.test.mjs", + "src/data/transitRouteNamesFrance.js", "src/data/transitFeeds.js", "src/data/transitFeeds.test.mjs", "src/data/transitProxy.js", diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index cfc18f7dd..ffa229c65 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -3304,6 +3304,8 @@ "src/data/transit.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/data/transitIcons.js", "src/data/transitPresetStyle.js", "src/layers/transit/controls.js", @@ -4287,6 +4289,8 @@ "src/data/railTimeZones.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/data/transitIcons.js", "src/data/transitPresetStyle.js", "src/layers/transit/controls.js", @@ -5139,6 +5143,8 @@ "src/data/gtfsRealtime.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitProxy.js", "src/sources/httpBody.js", @@ -5156,6 +5162,8 @@ "src/data/gtfsRealtime.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitProxy.js", "src/sources/transitHistory.js" @@ -5190,6 +5198,8 @@ "src/data/transit.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/data/transitIcons.js", "src/data/transitPresetStyle.js", "src/data/transitProxy.js", @@ -5253,6 +5263,8 @@ "src/data/inputOwnership.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitIcons.js", "src/data/transitPresetStyle.js", @@ -5325,6 +5337,8 @@ "src/data/railNoPositions.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitProxy.js", "src/data/gtfsRealtime.js", @@ -5355,6 +5369,8 @@ "src/data/railNoPositions.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitProxy.js", "src/data/gtfsRealtime.js", @@ -5371,6 +5387,8 @@ "modules": [ "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/geoDistance.js", "src/data/transitProxy.js", "src/data/gtfsRealtime.js", @@ -5638,6 +5656,8 @@ "src/layers/transit/policy.js", "src/data/transitFeeds.js", "src/data/rioRouteNames.js", + "src/data/transitFeedsFrance.js", + "src/data/transitRouteNamesFrance.js", "src/layers/transit/movement.js", "src/data/contactPlayback.js", "src/data/layerSnapshot.js", diff --git a/src/data/transitFeeds.js b/src/data/transitFeeds.js index 42f399d77..c055df563 100644 --- a/src/data/transitFeeds.js +++ b/src/data/transitFeeds.js @@ -1,5 +1,7 @@ import { greatCircleKm } from '../geoDistance.js'; import { RIO_ROUTE_NAMES } from './rioRouteNames.js'; +import { FRANCE_TRANSIT_FEED_ROWS } from './transitFeedsFrance.js'; +import { FRANCE_ROUTE_NAMES } from './transitRouteNamesFrance.js'; /** * @module transitFeeds @@ -10,6 +12,8 @@ import { RIO_ROUTE_NAMES } from './rioRouteNames.js'; * `/api/transit/vehicles/` for a registered id (see SECURITY.md: proxies * never fetch client-supplied URLs). Adding a feed means adding a row here, * a DATA_SOURCES.md row with its license, and a credit in dataCredits.js. + * The French networks listed on transport.data.gouv.fr are rows of the same + * shape in `transitFeedsFrance.js`, appended to the registry below. * * Admission rules for a feed: * - No key, token, or registration required (identify-yourself headers are @@ -562,6 +566,58 @@ function transitFeed(fields) { }); } +/** + * A French network row (`transitFeedsFrance.js`) as a registry entry. Every + * one is a bus network unless its row says otherwise: `routeModes` names the + * route ids that are trams or boats, and a route id it does not name stays a + * default, never a resolved "bus". The licence name, record link and terms + * note are built from the row's `licence`, `record`, `vehicles` and `note`. + * A feed with a table in + * `transitRouteNamesFrance.js` reads its line names from it; an id missing + * from the table renders unchanged. + * @param {object} row + * @returns {Readonly} + */ +function franceTransitFeed(row) { + const { routeModes, licence, record, vehicles, note, ...fields } = row; + const odbl = licence === 'odc-odbl'; + const names = Object.hasOwn(FRANCE_ROUTE_NAMES, row.id) + ? FRANCE_ROUTE_NAMES[row.id] + : null; + return transitFeed({ + defaultMode: 'bus', + ...fields, + license: odbl ? 'ODbL' : 'Licence Ouverte 2.0', + licenseUrl: `https://transport.data.gouv.fr/datasets/${record}`, + terms: { + quote: `licence: ${licence}`, + note: [ + `Dataset record’s licence field quoted; this is the record’s own GTFS-RT vehicle feed (${vehicles} vehicles on 2026-11-13).`, + odbl + ? 'ODbL asks for credit and share-alike on a derived database; the layer draws positions and redistributes no database.' + : '', + note ?? '', + ] + .filter(Boolean) + .join(' '), + }, + ...(routeModes + ? { + routeMode: (routeId) => + routeId && Object.hasOwn(routeModes, routeId) + ? routeModes[routeId] + : 'unknown', + } + : {}), + ...(names + ? { + routeLabel: (routeId) => + routeId && Object.hasOwn(names, routeId) ? names[routeId] : routeId, + } + : {}), + }); +} + /** * Registry of feeds. Order is presentation order in the stats/credit text. * `loadRadiusKm` is the distance from `center` inside which the feed is polled. @@ -1586,6 +1642,7 @@ export const TRANSIT_FEED_REGISTRY = Object.freeze([ }, defaultMode: 'bus', }), + ...FRANCE_TRANSIT_FEED_ROWS.map(franceTransitFeed), ]); /** diff --git a/src/data/transitFeeds.test.mjs b/src/data/transitFeeds.test.mjs index 75a902728..2e91a5e44 100644 --- a/src/data/transitFeeds.test.mjs +++ b/src/data/transitFeeds.test.mjs @@ -713,8 +713,13 @@ test('feeds that publish km/h are marked for conversion, and only those', () => .map((f) => f.id) .sort(); assert.deepEqual(kmh, [ + 'azalys-blois', 'carris-metropolitana-lisboa', + 'divia-dijon', + 'maelis-montlucon', 'mpm-toulon', + 'rubis-bourg-en-bresse', + 'setram-le-mans', 'tam-montpellier', ]); }); diff --git a/src/data/transitFeedsFrance.js b/src/data/transitFeedsFrance.js new file mode 100644 index 000000000..4d99af82e --- /dev/null +++ b/src/data/transitFeedsFrance.js @@ -0,0 +1,2021 @@ +/** + * @module transitFeedsFrance + * @description French networks for the Transit layer, as plain rows that + * `transitFeeds.js` turns into registry entries. + * + * Every row is a keyless GTFS-Realtime VehiclePositions feed listed on the + * national access point, transport.data.gouv.fr, whose dataset record states + * an open licence: `record` is that record's slug and `licence` its licence + * field (`lov2` is Licence Ouverte 2.0, `odc-odbl` is ODbL). Each feed + * answered with real vehicle positions on 2026-11-13, `vehicles` of them; `center` and `loadRadiusKm` come from the stops in + * the same record's GTFS. `routeModes` names the route ids that are not + * buses, taken from `route_type` in that GTFS. Line names for feeds that + * publish internal route keys are in `transitRouteNamesFrance.js`. + * + * Pure data: no imports. + */ + +/** @type {ReadonlyArray} */ +export const FRANCE_TRANSIT_FEED_ROWS = [ + { + id: 'capbus-agde', + name: 'Agde Cap’Bus', + operator: 'Communauté d’agglomération Hérault-Méditerranée (Cap’Bus)', + region: 'Agde, France', + center: { + lat: 43.309, + lon: 3.473, + }, + loadRadiusKm: 25, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=agdecapbus68429531', + licence: 'lov2', + record: 'gtfs-capbus', + vehicles: 6, + attribution: 'Cap’Bus – Communauté d’agglomération Hérault-Méditerranée', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'tempo-agen', + name: 'Agen Tempo', + operator: 'Agglomération d’Agen (Tempo Bus)', + region: 'Agen, France', + center: { + lat: 44.196, + lon: 0.634, + }, + loadRadiusKm: 20, + url: 'https://api.pysae.com/api/v4/groups/agen/gtfs-rt', + licence: 'lov2', + record: 'agen-gtfs-urbain', + vehicles: 33, + attribution: 'Tempo Bus – Agglomération d’Agen', + }, + { + id: 'alesy-ales', + name: 'Alès Alesy', + operator: 'Syndicat mixte transports publics du bassin d’Alès (Alesy)', + region: 'Alès, France', + center: { + lat: 44.132, + lon: 4.084, + }, + loadRadiusKm: 25, + url: 'https://alesy.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/ALESY-6574-4401-7572/bin', + licence: 'odc-odbl', + record: + 'horaires-theoriques-des-lignes-urbaines-et-interurbaines-sur-le-reseau-alesy-gtfs', + vehicles: 62, + attribution: 'Alesy – Syndicat mixte transports publics du bassin d’Alès', + }, + { + id: 'aleop-pays-de-la-loire', + name: 'Aléop coaches', + operator: 'Pays de la Loire (Aléop - Pays de la Loire)', + region: 'Pays de la Loire, France', + center: { + lat: 47.475, + lon: -0.891, + }, + loadRadiusKm: 140, + url: 'https://proxy.transport.data.gouv.fr/resource/aleop-pdl-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'arrets-horaires-et-circuits-des-lignes-de-transports-aleop-1', + vehicles: 103, + attribution: 'Aléop - Pays de la Loire – Pays de la Loire', + routeModes: { + YEU: 'ferry', + }, + note: 'Served by the national access point’s own proxy of the operator feed. Route `YEU` is the Île d’Yeu boat. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'ametis-amiens', + name: 'Amiens Ametis', + operator: 'Communauté d’agglomération Amiens Métropole (Ametis)', + region: 'Amiens, France', + center: { + lat: 49.888, + lon: 2.299, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/ametis-amiens-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'ametis', + vehicles: 95, + attribution: 'Ametis – Communauté d’agglomération Amiens Métropole', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'irigo-angers', + name: 'Angers Irigo', + operator: 'Communauté urbaine Angers Loire Métropole (Irigo)', + region: 'Angers, France', + center: { + lat: 47.475, + lon: -0.542, + }, + loadRadiusKm: 20, + url: 'https://ara-api.enroute.mobi/irigo/gtfs/vehicle-positions', + licence: 'odc-odbl', + record: 'angers-loire-metropole-reseau-irigo-gtfs-gtfs-rt-siri', + vehicles: 214, + attribution: 'Irigo – Communauté urbaine Angers Loire Métropole', + routeModes: { + A: 'tram', + B: 'tram', + C: 'tram', + }, + note: 'Lines A, B and C are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'envia-arles', + name: 'Arles Envia', + operator: + 'Communauté d’agglomération d’Arles-Crau-Camargue-Montagnette (Envia)', + region: 'Arles, France', + center: { + lat: 43.669, + lon: 4.639, + }, + loadRadiusKm: 35, + url: 'https://accm.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/ACCM-RSUD-4552-8552-5459/bin', + licence: 'lov2', + record: 'donnees-statiques-et-temps-reels-reseau-envia', + vehicles: 33, + attribution: + 'Envia – Communauté d’agglomération d’Arles-Crau-Camargue-Montagnette', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'artis-arras', + name: 'Arras Artis', + operator: 'Communauté urbaine d’Arras (Artis)', + region: 'Arras, France', + center: { + lat: 50.289, + lon: 2.775, + }, + loadRadiusKm: 15, + url: 'https://api.pysae.com/api/v4/groups/keolis-arras-8H8k/gtfs-rt', + licence: 'lov2', + record: 'reseau-de-transport-artis', + vehicles: 5, + attribution: 'Artis – Communauté urbaine d’Arras', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'toutenbus-aubenas', + name: 'Aubenas Tout’enbus', + operator: + 'Région Auvergne-Rhône-Alpes (CC du Bassin d’Aubenas) (Tout’enbus)', + region: 'Aubenas, France', + center: { + lat: 44.618, + lon: 4.389, + }, + loadRadiusKm: 15, + url: 'https://gtfs-rt.infra-hubup.fr/toutenbus/realtime', + licence: 'lov2', + record: 'donnees-theoriques-et-temps-reel', + vehicles: 5, + attribution: + 'Tout’enbus – Région Auvergne-Rhône-Alpes (CC du Bassin d’Aubenas)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'alliance-auch', + name: 'Auch Alliance', + operator: + 'Communauté d’agglomération Grand Auch Coeur de Gascogne (Alliance)', + region: 'Auch, France', + center: { + lat: 43.649, + lon: 0.591, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=auch-alliance', + licence: 'lov2', + record: 'gtfs-reseau-alliance-bus-grand-auch', + vehicles: 7, + attribution: + 'Alliance – Communauté d’agglomération Grand Auch Coeur de Gascogne', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'glazgo-auray', + name: 'Auray Glazgo', + operator: 'Communauté de communes Auray Quiberon Terre Atlantique (Glazgo)', + region: 'Auray, France', + center: { + lat: 47.669, + lon: -2.994, + }, + loadRadiusKm: 10, + url: 'https://api.pysae.com/api/v4/groups/glazgo/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-glazgo', + vehicles: 4, + attribution: + 'Glazgo – Communauté de communes Auray Quiberon Terre Atlantique', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'orizo-avignon', + name: 'Avignon Orizo', + operator: 'Communauté d’agglomération du Grand Avignon (COGA) (Orizo)', + region: 'Avignon, France', + center: { + lat: 43.951, + lon: 4.828, + }, + loadRadiusKm: 15, + url: 'https://api.maas-fr.cityway.fr/dataflow/vehicule-tc-tr/download?provider=ORIZO&dataFormat=gtfs-rt', + licence: 'lov2', + record: 'reseau-orizo-grand-avignon', + vehicles: 90, + attribution: 'Orizo – Communauté d’agglomération du Grand Avignon (COGA)', + routeModes: { + T1: 'tram', + }, + note: 'Line T1 is the tramway.', + }, + { + id: 'moca-barentin', + name: 'Barentin MOCA', + operator: 'Communauté de communes Caux - Austreberthe (MOCA)', + region: 'Barentin, France', + center: { + lat: 49.548, + lon: 0.952, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/moca-caux-austreberthe-gtfs-rt', + licence: 'lov2', + record: 'moca-communaute-de-communes-caux-austreberthe', + vehicles: 2, + attribution: 'MOCA – Communauté de communes Caux - Austreberthe', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13.', + }, + { + id: 'cote-et-bus-beaune', + name: 'Beaune Côte&Bus', + operator: + 'Communauté d’agglomération Beaune, Côte et Sud - Communauté Beaune-Chagny-Nolay (Côte&Bus)', + region: 'Beaune, France', + center: { + lat: 47.022, + lon: 4.838, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/beaune-cote-et-bus-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'offre-de-transport-de-la-c-a-beaune-cote-sud-gtfs', + vehicles: 5, + attribution: + 'Côte&Bus – Communauté d’agglomération Beaune, Côte et Sud - Communauté Beaune-Chagny-Nolay', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'corolis-beauvais', + name: 'Beauvais Corolis', + operator: 'Communauté d’agglomération du Beauvaisis (Corolis)', + region: 'Beauvais, France', + center: { + lat: 49.432, + lon: 2.089, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=COROLIS_URB&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-corolis-urbain-communaute-dagglomeration-du-beauvaisis', + vehicles: 23, + attribution: 'Corolis – Communauté d’agglomération du Beauvaisis', + }, + { + id: 'corolis-beauvaisis-interurbain', + name: 'Beauvaisis Corolis interurbain', + operator: 'Communauté d’agglomération du Beauvaisis (Corolis)', + region: 'Beauvais, France', + center: { + lat: 49.427, + lon: 2.085, + }, + loadRadiusKm: 25, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=COROLIS_INT&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-corolis-interurbain-communaute-dagglomeration-du-beauvaisis', + vehicles: 13, + attribution: 'Corolis – Communauté d’agglomération du Beauvaisis', + }, + { + id: 'lbus-bernay', + name: 'Bernay l’Bus', + operator: + 'Communauté de communes Intercom Bernay Terres de Normandie (l’Bus)', + region: 'Bernay, France', + center: { + lat: 49.09, + lon: 0.596, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?src=true&dataset=bernay', + licence: 'lov2', + record: 'reseau-transport-urbain-de-bernay', + vehicles: 1, + attribution: + 'l’Bus – Communauté de communes Intercom Bernay Terres de Normandie', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'azalys-blois', + name: 'Blois Azalys', + operator: 'Communauté d’agglomération de Blois "Agglopolys" (Azalys)', + region: 'Blois, France', + center: { + lat: 47.582, + lon: 1.313, + }, + loadRadiusKm: 20, + url: 'https://app.mecatran.com/utw/ws/gtfsfeed/vehicles/blois?apiKey=7a0d1031460836674e76041e7a78011813347f04', + licence: 'odc-odbl', + record: 'agglopolys-offre-theorique-mobilite-reseau-urbain-azalys-de-blois', + vehicles: 55, + attribution: 'Azalys – Communauté d’agglomération de Blois "Agglopolys"', + speedUnit: 'km/h', + note: 'Speeds are km/h: readings ran 6.63 times the measured movement on 2026-11-13, so the proxy converts them. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'rubis-bourg-en-bresse', + name: 'Bourg-en-Bresse Rubis', + operator: 'Communauté d’agglomération du Bassin de Bourg-en-Bresse (Rubis)', + region: 'Bourg-en-Bresse, France', + center: { + lat: 46.219, + lon: 5.221, + }, + loadRadiusKm: 30, + url: 'https://proxy.transport.data.gouv.fr/resource/rubis-bourg-en-bresse-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'rubis-grand-bourg-mobilites-offre-de-transport-theorique-et-temps-reel', + vehicles: 34, + attribution: + 'Rubis – Communauté d’agglomération du Bassin de Bourg-en-Bresse', + speedUnit: 'km/h', + note: 'Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 4.99 times the measured movement on 2026-11-13, so the proxy converts them. The feed carries no route ids.', + }, + { + id: 'ruban-bourgoin-jallieu', + name: 'Bourgoin-Jallieu Ruban', + operator: 'Communauté d’agglomération Porte de l’Isère (CAPI) (Ruban)', + region: 'Bourgoin-Jallieu, France', + center: { + lat: 45.614, + lon: 5.235, + }, + loadRadiusKm: 20, + url: 'https://app.pysae.com/api/v2/groups/keolis-9cc4/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-ruban', + vehicles: 28, + attribution: 'Ruban – Communauté d’agglomération Porte de l’Isère (CAPI)', + }, + { + id: 'bibus-brest', + name: 'Brest Bibus', + operator: 'Brest Métropole (Bibus)', + region: 'Brest, France', + center: { + lat: 48.399, + lon: -4.489, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'horaires-theoriques-et-temps-reel-des-bus-et-tramways-circulant-sur-le-territoire-de-brest-metropole', + vehicles: 113, + attribution: 'Bibus – Brest Métropole', + routeModes: { + A: 'tram', + B: 'tram', + }, + note: 'Served by the national access point’s own proxy of the operator feed. Lines A and B are the tramway; line C is the cable car, which has no mode of its own here and draws as a bus. Vehicles carry no timestamp of their own, so the feed header’s is used.', + }, + { + id: 'beemob-beziers', + name: 'Béziers beeMob', + operator: 'Communauté d’agglomération de Béziers-Méditerranée (beeMob)', + region: 'Béziers, France', + center: { + lat: 43.346, + lon: 3.238, + }, + loadRadiusKm: 15, + url: 'https://feed-beemob-beziers.ratpdev.com/GTFS-RT_vehiclePosition/gtfs-rt.bin', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-de-transports-beemob-beziers-mediterranee', + vehicles: 40, + attribution: 'beeMob – Communauté d’agglomération de Béziers-Méditerranée', + }, + { + id: 'palmbus-cannes', + name: 'Cannes Palm Bus', + operator: 'Communauté d’agglomération Cannes Pays de Lérins (Palmbus)', + region: 'Cannes, France', + center: { + lat: 43.56, + lon: 7.002, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/palmbus-cannes-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'horaires-theoriques-et-temps-reel-gtfs-gtfs-rt-du-reseau-palmbus-cannes-pays-de-lerins', + vehicles: 86, + attribution: 'Palmbus – Communauté d’agglomération Cannes Pays de Lérins', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'rtca-carcassonne', + name: 'Carcassonne RTCA', + operator: 'Communauté d’agglomération Carcassonne Agglo (RTCA)', + region: 'Carcassonne, France', + center: { + lat: 43.212, + lon: 2.362, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/rtca-carcassonne-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'reseau-rtca', + vehicles: 23, + attribution: 'RTCA – Communauté d’agglomération Carcassonne Agglo', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'rezobus-caux-seine', + name: 'Caux Seine Rezo’Bus', + operator: 'Communauté d’agglomération Caux Seine Agglo (REZO’BUS)', + region: 'Lillebonne, France', + center: { + lat: 49.524, + lon: 0.549, + }, + loadRadiusKm: 20, + url: 'https://app.pysae.com/api/v4/groups/caux-seine-agglo/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-caux-seine-mobilite-rezobus', + vehicles: 2, + attribution: 'REZO’BUS – Communauté d’agglomération Caux Seine Agglo', + }, + { + id: 'cmonbus-cavaillon', + name: 'Cavaillon C’mon bus', + operator: 'Communauté d’agglomération Luberon Monts de Vaucluse (Cmonbus)', + region: 'Cavaillon, France', + center: { + lat: 43.838, + lon: 5.042, + }, + loadRadiusKm: 10, + url: 'https://app.pysae.com/api/v2/groups/cavaillon/gtfs-rt', + licence: 'lov2', + record: 'gtfs-reseau-cmonbus', + vehicles: 11, + attribution: + 'Cmonbus – Communauté d’agglomération Luberon Monts de Vaucluse', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'agglobus-cayenne', + name: 'Cayenne Agglo’bus', + operator: 'Communauté d’agglomération du Centre Littoral (Agglobus)', + region: 'Cayenne, French Guiana', + center: { + lat: 4.917, + lon: -52.319, + }, + loadRadiusKm: 15, + url: 'https://cacl.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/CACL-1823-7594-1349/bin', + licence: 'lov2', + record: 'agence-agglobus', + vehicles: 44, + attribution: 'Agglobus – Communauté d’agglomération du Centre Littoral', + }, + { + id: 'chamonix-bus', + name: 'Chamonix buses', + operator: 'Communauté de communes de la Vallée de Chamonix-Mont-Blanc', + region: 'Chamonix-Mont-Blanc, France', + center: { + lat: 45.92, + lon: 6.856, + }, + loadRadiusKm: 20, + url: 'https://api.pysae.com/api/v4/groups/chamonix-bus/gtfs-rt', + licence: 'lov2', + record: 'gtfs-reseau-chamonix-mobilite', + vehicles: 11, + attribution: 'Communauté de communes de la Vallée de Chamonix-Mont-Blanc', + }, + { + id: 'airemob-chantilly', + name: 'Chantilly Aire’MOB', + operator: 'Communauté de communes de l’Aire Cantilienne (Aire’MOB)', + region: 'Chantilly, France', + center: { + lat: 49.166, + lon: 2.441, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=CCAC&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-airemob-communaute-de-communes-de-laire-cantilienne', + vehicles: 2, + attribution: 'Aire’MOB – Communauté de communes de l’Aire Cantilienne', + }, + { + id: 'cap-cotentin-cherbourg', + name: 'Cherbourg Cap Cotentin', + operator: 'Communauté d’agglomération du Cotentin (Cap Cotentin)', + region: 'Cherbourg-en-Cotentin, France', + center: { + lat: 49.634, + lon: -1.628, + }, + loadRadiusKm: 30, + url: 'https://app.pysae.com/api/v2/groups/transdev-cotentin/gtfs-rt', + licence: 'lov2', + record: + 'horaires-theoriques-et-en-temps-reel-des-bus-et-autocars-circulant-sur-le-reseau-cap-cotentin', + vehicles: 53, + attribution: 'Cap Cotentin – Communauté d’agglomération du Cotentin', + }, + { + id: 'cvl-mobilite-chinon', + name: 'Chinon CVL Mobilité', + operator: 'Communauté de communes Chinon, Vienne et Loire (CVL Mobilité)', + region: 'Chinon, France', + center: { + lat: 47.182, + lon: 0.215, + }, + loadRadiusKm: 15, + url: 'https://cvlmobilite.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/CVLMOBILITE-4423-7657-4931/bin', + licence: 'lov2', + record: + 'reseau-cvlmobilite-plan-de-transport-theorique-ligne-a-format-gtfs', + vehicles: 7, + attribution: + 'CVL Mobilité – Communauté de communes Chinon, Vienne et Loire', + }, + { + id: 'lebus-clermont-oise', + name: 'Clermont (Oise) Le Bus', + operator: 'Communauté de communes du Clermontois (Le Bus)', + region: 'Clermont, Oise, France', + center: { + lat: 49.379, + lon: 2.414, + }, + loadRadiusKm: 15, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=LEBUS&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-le-bus-communaute-de-communes-du-clermontois', + vehicles: 4, + attribution: 'Le Bus – Communauté de communes du Clermontois', + }, + { + id: 'tic-compiegne', + name: 'Compiègne TIC', + operator: + 'Communauté d’agglomération de la Région de Compiègne et de la Basse Automne (TIC)', + region: 'Compiègne, France', + center: { + lat: 49.41, + lon: 2.817, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=TIC_URB&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-tic-urbain-communaute-dagglomeration-de-la-region-de-compiegne-et-de-la-basse-automne', + vehicles: 19, + attribution: + 'TIC – Communauté d’agglomération de la Région de Compiègne et de la Basse Automne', + }, + { + id: 'tic-compiegne-interurbain', + name: 'Compiègne TIC interurbain', + operator: + 'Communauté d’agglomération de la Région de Compiègne et de la Basse Automne (TIC)', + region: 'Compiègne, France', + center: { + lat: 49.394, + lon: 2.795, + }, + loadRadiusKm: 15, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=TIC_INT&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-tic-interurbain-communaute-dagglomeration-de-la-region-de-compiegne-et-de-la-basse-automne', + vehicles: 21, + attribution: + 'TIC – Communauté d’agglomération de la Région de Compiègne et de la Basse Automne', + }, + { + id: 'papillon-cosne', + name: 'Cosne-sur-Loire Papillon', + operator: 'Communauté de communes Coeur de Loire (Papillon)', + region: 'Cosne-Cours-sur-Loire, France', + center: { + lat: 47.406, + lon: 2.928, + }, + loadRadiusKm: 10, + url: 'https://api.pysae.com/api/v4/groups/coeur-de-loire/gtfs-rt', + licence: 'lov2', + record: 'donnees-du-reseau-de-bus-papillon-a-cosne-cours-sur-loire', + vehicles: 2, + attribution: 'Papillon – Communauté de communes Coeur de Loire', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'axo-creil', + name: 'Creil AXO', + operator: 'Communauté d’agglomération Creil Sud Oise (AXO)', + region: 'Creil, France', + center: { + lat: 49.26, + lon: 2.464, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=AXO&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-axo-communaute-dagglomeration-creil-sud-oise', + vehicles: 28, + attribution: 'AXO – Communauté d’agglomération Creil Sud Oise', + }, + { + id: 'mobi-crepy-en-valois', + name: 'Crépy-en-Valois Mobi', + operator: 'Communauté de communes du Pays de Valois (Mobi)', + region: 'Crépy-en-Valois, France', + center: { + lat: 49.224, + lon: 2.895, + }, + loadRadiusKm: 25, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=CYPRE&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-mobi-communaute-de-communes-du-pays-de-valois', + vehicles: 5, + attribution: 'Mobi – Communauté de communes du Pays de Valois', + }, + { + id: 'taco-coeur-du-var', + name: 'Cœur du Var Le TACO', + operator: 'Région Provence-Alpes-Côte d’Azur (CC Coeur du Var) (Le TACO)', + region: 'Le Luc, France', + center: { + lat: 43.393, + lon: 6.315, + }, + loadRadiusKm: 10, + url: 'https://api.pysae.com/api/v4/groups/transdev-var-taco/gtfs-rt', + licence: 'lov2', + record: 'le-taco-1', + vehicles: 1, + attribution: + 'Le TACO – Région Provence-Alpes-Côte d’Azur (CC Coeur du Var)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'deepmob-dieppe', + name: 'Dieppe DeepMob', + operator: 'Communauté d’agglomération de la Région Dieppoise (DeepMob)', + region: 'Dieppe, France', + center: { + lat: 49.912, + lon: 1.083, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/deepmob-dieppe-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'deepmob-dieppe-maritime', + vehicles: 12, + attribution: 'DeepMob – Communauté d’agglomération de la Région Dieppoise', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'divia-dijon', + name: 'Dijon Divia', + operator: 'Dijon Métropole (DiviaMobilités)', + region: 'Dijon, France', + center: { + lat: 47.318, + lon: 5.044, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/divia-dijon-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'gtfs-diviamobilites', + vehicles: 149, + attribution: 'DiviaMobilités – Dijon Métropole', + speedUnit: 'km/h', + routeModes: { + '4-T1': 'tram', + '4-T2': 'tram', + }, + note: 'Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 4.42 times the measured movement on 2026-11-13, so the proxy converts them. Lines T1 and T2 are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'grandole-dole', + name: 'Dole Grandole Mobilités', + operator: 'Communauté d’agglomération du Grand Dole (Grandole Mobilités)', + region: 'Dole, France', + center: { + lat: 47.09, + lon: 5.487, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/tgd-dole-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'reseau-de-transport-du-grand-dole', + vehicles: 18, + attribution: + 'Grandole Mobilités – Communauté d’agglomération du Grand Dole', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'tedbus-draguignan', + name: 'Draguignan TEDbus', + operator: 'Dracénie Provence Verdon Agglomération (TEDbus)', + region: 'Draguignan, France', + center: { + lat: 43.53, + lon: 6.463, + }, + loadRadiusKm: 15, + url: 'https://pysae.com/api/v2/groups/draguignan/gtfs-rt', + licence: 'lov2', + record: + 'reseau-tedbus-lignes-regulieres-dracenie-provence-verdon-agglomeration', + vehicles: 15, + attribution: 'TEDbus – Dracénie Provence Verdon Agglomération', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'lebus-figeac', + name: 'Figeac Le Bus', + operator: 'Région Occitanie (CC Grand-Figeac) (Figeac - Le Bus)', + region: 'Figeac, France', + center: { + lat: 44.609, + lon: 2.03, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=figeac', + licence: 'odc-odbl', + record: 'horaires-theoriques-du-reseau-figeac-le-bus-gtfs', + vehicles: 3, + attribution: 'Figeac - Le Bus – Région Occitanie (CC Grand-Figeac)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'nemus-flers', + name: 'Flers Némus', + operator: 'Communauté d’agglomération Flers Agglo (Nemus)', + region: 'Flers, France', + center: { + lat: 48.745, + lon: -0.573, + }, + loadRadiusKm: 10, + url: 'https://flers-agglo.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/FLERS-5197-3181-6523/bin?network=104', + licence: 'lov2', + record: 'donnees-theoriques-du-reseau-urbain-de-flers-agglo-au-format-gtfs', + vehicles: 5, + attribution: 'Nemus – Communauté d’agglomération Flers Agglo', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'surf-fougeres', + name: 'Fougères SURF', + operator: 'Communauté d’agglomération Fougères Agglomération (SURF)', + region: 'Fougères, France', + center: { + lat: 48.352, + lon: -1.196, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/surf-fougeres-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: 'reseau-urbain-surf', + vehicles: 8, + attribution: 'SURF – Communauté d’agglomération Fougères Agglomération', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'lebus-frejus', + name: 'Fréjus–Saint-Raphaël Le Bus', + operator: 'Estérel Côte d’Azur Agglomération (Le Bus)', + region: 'Fréjus, France', + center: { + lat: 43.434, + lon: 6.756, + }, + loadRadiusKm: 15, + url: 'https://api.pysae.com/api/v4/groups/agglobus-cavem/gtfs-rt', + licence: 'lov2', + record: 'reseau-transport-urbain-le-bus', + vehicles: 31, + attribution: 'Le Bus – Estérel Côte d’Azur Agglomération', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'agglo-en-bus-gap', + name: 'Gap L’Agglo en Bus', + operator: 'Communauté d’agglomération Gap-Tallard-Durance (L’Agglo en Bus)', + region: 'Gap, France', + center: { + lat: 44.557, + lon: 6.074, + }, + loadRadiusKm: 10, + url: 'https://gtfs-rt.infra-hubup.fr/cagtd/realtime', + licence: 'lov2', + record: + 'horaires-theoriques-et-temps-reels-du-reseau-de-transports-lagglo-en-bus-communaute-dagglomeration-gap-tallard-durance', + vehicles: 11, + attribution: + 'L’Agglo en Bus – Communauté d’agglomération Gap-Tallard-Durance', + }, + { + id: 'guingamp-paimpol', + name: 'Guingamp-Paimpol Mobilité', + operator: + 'Communauté d’agglomération Guingamp-Paimpol Agglomération de l’Armor à l’Argoat (Guingamp-Paimpol Mobilité)', + region: 'Guingamp, France', + center: { + lat: 48.573, + lon: -3.127, + }, + loadRadiusKm: 35, + url: 'https://proxy.transport.data.gouv.fr/resource/axeo-guingamp-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: 'reseau-urbain-scolaire-guingamp-paimpol-mobilite', + vehicles: 13, + attribution: + 'Guingamp-Paimpol Mobilité – Communauté d’agglomération Guingamp-Paimpol Agglomération de l’Armor à l’Argoat', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'lila-presquile-guerande', + name: 'Guérande Lila Presqu’île', + operator: + 'Communauté d’agglomération de la Presqu’île de Guérande Atlantique (CAP ATLANTIQUE) (Lila Presqu’ile)', + region: 'Guérande, France', + center: { + lat: 47.355, + lon: -2.371, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/lila-presquile-cap-atlantique-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: 'reseau-urbain-scolaire-lila', + vehicles: 20, + attribution: + 'Lila Presqu’ile – Communauté d’agglomération de la Presqu’île de Guérande Atlantique (CAP ATLANTIQUE)', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'ritmo-haguenau', + name: 'Haguenau Ritmo', + operator: 'Communauté d’agglomération de Haguenau (Ritmo)', + region: 'Haguenau, France', + center: { + lat: 48.803, + lon: 7.777, + }, + loadRadiusKm: 20, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=haguenau', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-ritmo-haguenau-gtfs-gtfs-rt', + vehicles: 20, + attribution: 'Ritmo – Communauté d’agglomération de Haguenau', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'hopbus-hazebrouck', + name: 'Hazebrouck Hop Bus', + operator: 'Communauté d’agglomération Coeur de Flandre (Hop Bus)', + region: 'Hazebrouck, France', + center: { + lat: 50.732, + lon: 2.552, + }, + loadRadiusKm: 25, + url: 'https://gtfs-rt.infra-hubup.fr/hopbus/realtime', + licence: 'lov2', + record: 'hop-bus-le-reseau-100-gratuit-du-coeur-de-flandre', + vehicles: 8, + attribution: 'Hop Bus – Communauté d’agglomération Coeur de Flandre', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'hobus-honfleur', + name: 'Honfleur Hobus', + operator: 'Communauté de communes du Pays de Honfleur-Beuzeville (Hobus)', + region: 'Honfleur, France', + center: { + lat: 49.41, + lon: 0.24, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/hobus-honfleur-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'hobus-honfleur', + vehicles: 2, + attribution: + 'Hobus – Communauté de communes du Pays de Honfleur-Beuzeville', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'cherpa-issoire', + name: 'Issoire Cherpa', + operator: 'Agglo Pays d’Issoire (Cherpa)', + region: 'Issoire, France', + center: { + lat: 45.546, + lon: 3.249, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?src=true&dataset=agglopaysdissoire', + licence: 'lov2', + record: 'cherpa-horaires-lignes-regulieres', + vehicles: 1, + attribution: 'Cherpa – Agglo Pays d’Issoire', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'lisatis-lavaur', + name: 'Lavaur Lisatis', + operator: 'Région Occitanie (CC Tarn-Agout) (LISATIS)', + region: 'Lavaur, France', + center: { + lat: 43.699, + lon: 1.812, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=lavaur', + licence: 'lov2', + record: 'reseau-de-transport-urbain-lisatis', + vehicles: 3, + attribution: 'LISATIS – Région Occitanie (CC Tarn-Agout)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'setram-le-mans', + name: 'Le Mans SETRAM', + operator: 'Communauté urbaine Le Mans Métropole (SETRAM)', + region: 'Le Mans, France', + center: { + lat: 48.001, + lon: 0.2, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/setram-lemans-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'gtfs-du-reseau-des-transports-bus-et-tramway-setram-circulant-sur-le-territoire-le-mans-metropole', + vehicles: 102, + attribution: 'SETRAM – Communauté urbaine Le Mans Métropole', + speedUnit: 'km/h', + routeModes: { + T1: 'tram', + T2: 'tram', + }, + note: 'Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 3.67 times the measured movement on 2026-11-13, so the proxy converts them. Lines T1 and T2 are the tramway.', + }, + { + id: 'carsud-reunion', + name: 'Le Tampon CarSud', + operator: 'Communauté d’agglomération du Sud (CarSud)', + region: 'Le Tampon, La Réunion, France', + center: { + lat: -21.29, + lon: 55.558, + }, + loadRadiusKm: 20, + url: 'https://zenbus.net/gtfs/rt/poll.proto?src=true&dataset=carsud-reunion', + licence: 'lov2', + record: 'referentiel-topologique-reseau-carsud', + vehicles: 22, + attribution: 'CarSud – Communauté d’agglomération du Sud', + }, + { + id: 'libellule-liancourt', + name: 'Liancourt Le Libellule', + operator: 'Communauté de communes du Liancourtois (Libellule)', + region: 'Liancourt, France', + center: { + lat: 49.328, + lon: 2.46, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=LELIBELLULE&dataFormat=gtfs-rt', + licence: 'odc-odbl', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-le-libellule-communaute-du-liancourtois-la-vallee-doree', + vehicles: 1, + attribution: 'Libellule – Communauté de communes du Liancourtois', + }, + { + id: 'astrobus-lisieux', + name: 'Lisieux Astrobus', + operator: 'Communauté d’agglomération Lisieux Normandie (Astrobus)', + region: 'Lisieux, France', + center: { + lat: 49.088, + lon: 0.218, + }, + loadRadiusKm: 30, + url: 'https://proxy.transport.data.gouv.fr/resource/astrobus-lisieux-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'astrobus-lisieux-normandie', + vehicles: 6, + attribution: 'Astrobus – Communauté d’agglomération Lisieux Normandie', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'tgl-longwy', + name: 'Longwy TGL', + operator: + 'Syndicat mixte intercommunal des transports de l’agglomération de Longwy (TGL)', + region: 'Longwy, France', + center: { + lat: 49.514, + lon: 5.771, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/tgl-longwy-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'horaires-theoriques-du-reseau-tgl', + vehicles: 21, + attribution: + 'TGL – Syndicat mixte intercommunal des transports de l’agglomération de Longwy', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'ole-lunel', + name: 'Lunel Olé', + operator: 'Communauté d’agglomération Lunel Agglo (Olé)', + region: 'Lunel, France', + center: { + lat: 43.683, + lon: 4.118, + }, + loadRadiusKm: 20, + url: 'https://ole.plateforme-2cloud.com/api/gtfsrt/2.0/vehiclepositions/OLE-2187-9024-3517/bin', + licence: 'lov2', + record: 'offre-de-transport-du-reseau-ole-lunel-agglo-gtfs-et-gtfs-rt', + vehicles: 2, + attribution: 'Olé – Communauté d’agglomération Lunel Agglo', + }, + { + id: 'luneo-luneville', + name: 'Lunéville Lunéo', + operator: 'PETR du Pays du Lunévillois (Lunéo)', + region: 'Lunéville, France', + center: { + lat: 48.592, + lon: 6.504, + }, + loadRadiusKm: 10, + url: 'https://pysae.com/api/v2/groups/luneo/gtfs-rt', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-lulu-en-bus-ex-luneo-gtfs-gtfs-rt', + vehicles: 2, + attribution: 'Lunéo – PETR du Pays du Lunévillois', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'martinique-centre', + name: 'Martinique Centre (CACEM)', + operator: 'Martinique Transport (de la zone Centre (CACEM))', + region: 'Fort-de-France, Martinique, France', + center: { + lat: 14.628, + lon: -61.055, + }, + loadRadiusKm: 15, + url: 'https://ext.saemartinique.ovh:4435/ProfilGtfsRt2_0RSProducer-Centre/VehiclePosition.pb', + licence: 'lov2', + record: 'gtfs-urbain-de-la-zone-centre', + vehicles: 28, + attribution: 'de la zone Centre (CACEM) – Martinique Transport', + }, + { + id: 'martinique-sud', + name: 'Martinique Sud (CAESM)', + operator: 'Martinique Transport (de la zone Sud (CAESM))', + region: 'Southern Martinique, France', + center: { + lat: 14.528, + lon: -60.919, + }, + loadRadiusKm: 20, + url: 'https://api.pysae.com/api/v4/groups/sud_lib/gtfs-rt', + licence: 'lov2', + record: 'gtfs-urbain-de-la-zone-sud', + vehicles: 42, + attribution: 'de la zone Sud (CAESM) – Martinique Transport', + routeModes: { + 4001: 'ferry', + 4002: 'ferry', + 4003: 'ferry', + 4004: 'ferry', + }, + note: 'Routes 4001 to 4004 are the Vedettes boats.', + }, + { + id: 'maybus-mayenne', + name: 'Mayenne May’bus', + operator: 'Communauté de communes Mayenne Communauté (May’bus)', + region: 'Mayenne, France', + center: { + lat: 48.303, + lon: -0.617, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=maybus', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-maybus-mayenne-gtfs-gtfs-rt', + vehicles: 2, + attribution: 'May’bus – Communauté de communes Mayenne Communauté', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'tum-mende', + name: 'Mende TUM', + operator: 'Région Occitanie (CC Coeur de Lozère) (TUM)', + region: 'Mende, France', + center: { + lat: 44.523, + lon: 3.497, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=MAX', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-max-mende-mobilites-bus-gtfs-gtfs-rt', + vehicles: 1, + attribution: 'TUM – Région Occitanie (CC Coeur de Lozère)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'zest-menton', + name: 'Menton Zest', + operator: 'Communauté d’agglomération de la Riviera Française (Zest)', + region: 'Menton, France', + center: { + lat: 43.781, + lon: 7.478, + }, + loadRadiusKm: 40, + url: 'https://proxy.transport.data.gouv.fr/resource/zest-menton-riviera-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'horaires-reseau-zest', + vehicles: 22, + attribution: 'Zest – Communauté d’agglomération de la Riviera Française', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'lemet-metz', + name: 'Metz Le Met’', + operator: 'Metz Métropole (Le Met’)', + region: 'Metz, France', + center: { + lat: 49.111, + lon: 6.17, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/lemet-metz-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'fichiers-gtfs-eurometropole-de-metz', + vehicles: 140, + attribution: 'Le Met’ – Metz Métropole', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'maelis-montlucon', + name: 'Montluçon Maelis', + operator: 'Communauté d’agglomération Montluçon Communauté (Maelis)', + region: 'Montluçon, France', + center: { + lat: 46.342, + lon: 2.592, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/maelis-montlucon-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-reel-du-reseau-maelis-montlucon-communaute-gtfs', + vehicles: 15, + attribution: 'Maelis – Communauté d’agglomération Montluçon Communauté', + speedUnit: 'km/h', + note: 'Served by the national access point’s own proxy of the operator feed. Speeds are km/h: readings ran 3.3 times the measured movement on 2026-11-13, so the proxy converts them. The feed carries no route ids.', + }, + { + id: 'montelibus-montelimar', + name: 'Montélimar Montélibus', + operator: + 'Communauté d’agglomération Montélimar Agglomération (Montelibus)', + region: 'Montélimar, France', + center: { + lat: 44.558, + lon: 4.759, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/montelibus-montelimar-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'horaires-montelibus', + vehicles: 20, + attribution: + 'Montelibus – Communauté d’agglomération Montélimar Agglomération', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'aleo-moulins', + name: 'Moulins Aléo', + operator: 'Communauté d’agglomération Moulins Communauté (Aléo)', + region: 'Moulins, France', + center: { + lat: 46.564, + lon: 3.333, + }, + loadRadiusKm: 15, + url: 'https://api.pysae.com/api/v4/groups/moulins/gtfs-rt', + licence: 'lov2', + record: 'donnees-du-reseau-de-transport-aleo', + vehicles: 16, + attribution: 'Aléo – Communauté d’agglomération Moulins Communauté', + }, + { + id: 'citibus-narbonne', + name: 'Narbonne Citibus', + operator: 'Communauté d’agglomération Le Grand Narbonne (Citibus)', + region: 'Narbonne, France', + center: { + lat: 43.183, + lon: 3.003, + }, + loadRadiusKm: 35, + url: 'https://feed-citibus-narbonne.ratpdev.com/GTFS-RT/gtfs-rt.bin', + licence: 'lov2', + record: 'offre-de-transport-communaute-dagglomeration-le-grand-narbonne', + vehicles: 54, + attribution: 'Citibus – Communauté d’agglomération Le Grand Narbonne', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'lignes-dazur-nice', + name: 'Nice Lignes d’Azur', + operator: 'Métropole Nice Côte d’Azur (Lignes d’Azur)', + region: 'Nice, France', + center: { + lat: 43.715, + lon: 7.238, + }, + loadRadiusKm: 35, + url: 'https://ara-api.enroute.mobi/rla/gtfs/vehicle-positions', + licence: 'lov2', + record: + 'donnees-statiques-et-dynamiques-du-reseau-de-transport-lignes-dazur', + vehicles: 241, + attribution: 'Lignes d’Azur – Métropole Nice Côte d’Azur', + routeModes: { + L1: 'tram', + L2: 'tram', + L3: 'tram', + }, + note: 'Lines L1, L2 and L3 are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'nosbus-nogent-le-rotrou', + name: 'Nogent-le-Rotrou Nosbus', + operator: 'Région Centre-Val de Loire (CC du Perche) (Nosbus)', + region: 'Nogent-le-Rotrou, France', + center: { + lat: 48.318, + lon: 0.818, + }, + loadRadiusKm: 10, + url: 'https://api.pysae.com/api/v4/groups/nosbus/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-nosbus', + vehicles: 2, + attribution: 'Nosbus – Région Centre-Val de Loire (CC du Perche)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'libbus-noyon', + name: 'Noyon Lib’Bus', + operator: 'Communauté de communes du Pays Noyonnais (Lib’Bus)', + region: 'Noyon, France', + center: { + lat: 49.581, + lon: 3.002, + }, + loadRadiusKm: 10, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=LIBBUS&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-libbus-communaute-de-communes-du-pays-noyonnais', + vehicles: 2, + attribution: 'Lib’Bus – Communauté de communes du Pays Noyonnais', + }, + { + id: 'navette-oloron', + name: 'Oloron-Sainte-Marie La Navette', + operator: + 'Communauté de communes du Haut Béarn (La Navette Oloron Sainte-Marie)', + region: 'Oloron-Sainte-Marie, France', + center: { + lat: 43.19, + lon: -0.613, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=oloron', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-oloron-sainte-marie-la-navette-gtfs-gtfs-rt', + vehicles: 2, + attribution: + 'La Navette Oloron Sainte-Marie – Communauté de communes du Haut Béarn', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'tcvo-orange', + name: 'Orange TCVO', + operator: 'Pays d’Orange en Provence (TCVO)', + region: 'Orange, France', + center: { + lat: 44.133, + lon: 4.814, + }, + loadRadiusKm: 10, + url: 'https://tcvo.2cloud.app/api/gtfsrt/2.0/vehiclepositions/TCVO-8420-1937-5506/bin', + licence: 'odc-odbl', + record: 'transport-en-commun-de-la-ville-dorange-ccpop-1', + vehicles: 5, + attribution: 'TCVO – Pays d’Orange en Provence', + }, + { + id: 'transpor-pays-de-lor', + name: 'Pays de l’Or Transp’Or', + operator: 'Communauté d’agglomération du Pays de l’Or (Transp’Or)', + region: 'Mauguio, France', + center: { + lat: 43.572, + lon: 4.006, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/transpor-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'offre-transport-en-commun-du-reseau-transpor-gtfs', + vehicles: 6, + attribution: 'Transp’Or – Communauté d’agglomération du Pays de l’Or', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'pays-des-ecrins', + name: 'Pays des Écrins buses', + operator: 'Communauté de communes du Pays des Ecrins', + region: 'L’Argentière-la-Bessée, France', + center: { + lat: 44.824, + lon: 6.5, + }, + loadRadiusKm: 20, + url: 'https://app.pysae.com/api/v2/groups/pays-des-ecrins/gtfs-rt', + licence: 'lov2', + record: 'transports-en-commun-pays-des-ecrins', + vehicles: 2, + attribution: 'Communauté de communes du Pays des Ecrins', + }, + { + id: 'sankeo-perpignan', + name: 'Perpignan Sankéo', + operator: 'Communauté urbaine Perpignan Méditerranée Métropole (Sankéo)', + region: 'Perpignan, France', + center: { + lat: 42.698, + lon: 2.897, + }, + loadRadiusKm: 20, + url: 'https://zenbus.net/gtfs/rt/poll.proto?src=true&dataset=sankeo-scolaire', + licence: 'lov2', + record: 'gtfs-sankeo', + vehicles: 50, + attribution: 'Sankéo – Communauté urbaine Perpignan Méditerranée Métropole', + }, + { + id: 'tohm-pont-sainte-maxence', + name: 'Pont-Sainte-Maxence TOHM', + operator: 'Communauté de communes des Pays d’Oise et d’Halatte (TOHM)', + region: 'Pont-Sainte-Maxence, France', + center: { + lat: 49.302, + lon: 2.593, + }, + loadRadiusKm: 15, + url: 'https://api.oisemob.cityway.fr/dataflow/vehicule-tc-tr/download?provider=TOHM&dataFormat=gtfs-rt', + licence: 'lov2', + record: + 'donnees-theoriques-et-temps-reel-du-reseau-tohm-communaute-de-communes-des-pays-doise-et-dhalatte', + vehicles: 5, + attribution: 'TOHM – Communauté de communes des Pays d’Oise et d’Halatte', + }, + { + id: 'pondibus-pontivy', + name: 'Pontivy PondiBUS', + operator: 'Communauté de communes Pontivy Communauté (PondiBUS)', + region: 'Pontivy, France', + center: { + lat: 48.067, + lon: -2.965, + }, + loadRadiusKm: 10, + url: 'https://pysae.com/api/v2/groups/pondibus/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-pondibus', + vehicles: 2, + attribution: 'PondiBUS – Communauté de communes Pontivy Communauté', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'a-citadina-porto-vecchio', + name: 'Porto-Vecchio A Citadina', + operator: 'Communauté de communes du Sud Corse (A Citadina)', + region: 'Porto-Vecchio, Corsica, France', + center: { + lat: 41.591, + lon: 9.278, + }, + loadRadiusKm: 10, + url: 'https://pysae.com/api/v2/groups/porto-vecchio/gtfs-rt', + licence: 'lov2', + record: 'reseau-a-citadina-porto-vecchio-horaires-theoriques-et-temps-reel', + vehicles: 2, + attribution: 'A Citadina – Communauté de communes du Sud Corse', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'qub-quimper', + name: 'Quimper QUB', + operator: 'Communauté d’agglomération Quimper Bretagne Occidentale (Qub)', + region: 'Quimper, France', + center: { + lat: 48.005, + lon: -4.095, + }, + loadRadiusKm: 20, + url: 'https://feed-qub-quimper.ratpdev.com/GTFS-RT_vehiclePosition/gtfs-rt.bin', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-du-reseau-de-transports-qub-transports-urbains-de-quimper-bretagne-occidentale', + vehicles: 49, + attribution: + 'Qub – Communauté d’agglomération Quimper Bretagne Occidentale', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'rlv-riom', + name: 'Riom RLV Mobilités', + operator: + 'Communauté d’agglomération Riom Limagne et Volcans (RLV Mobilités)', + region: 'Riom, France', + center: { + lat: 45.888, + lon: 3.107, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/riom-rlv-mobilites-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'horaires-rlv-mobilites', + vehicles: 7, + attribution: + 'RLV Mobilités – Communauté d’agglomération Riom Limagne et Volcans', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'titus-rosny-sous-bois', + name: 'Rosny-sous-Bois Titus', + operator: 'Île-de-France Mobilités (Titus)', + region: 'Rosny-sous-Bois, France', + center: { + lat: 48.876, + lon: 2.487, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=reseau-titus', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-titus-rosny-sous-bois-gtfs-gtfs-rt', + vehicles: 2, + attribution: 'Titus – Île-de-France Mobilités', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'jybus-rumilly', + name: 'Rumilly J’ybus', + operator: 'Communauté de communes Rumilly Terre de Savoie (Jybus)', + region: 'Rumilly, France', + center: { + lat: 45.871, + lon: 5.955, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/jybus-rumilly-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'offre-de-transports-jybus-a-rumilly', + vehicles: 6, + attribution: 'Jybus – Communauté de communes Rumilly Terre de Savoie', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'remi-centre-val-de-loire', + name: 'Rémi coaches', + operator: 'Centre-Val de Loire (Rémi)', + region: 'Centre-Val de Loire, France', + center: { + lat: 47.642, + lon: 1.499, + }, + loadRadiusKm: 135, + url: 'https://api.okina.fr/gateway/cvl/gtfsrt/vehicle-positions?useoriginalId=true', + licence: 'odc-odbl', + record: 'remi-offre-theorique-mobilite-reseau-interurbain-regional', + vehicles: 100, + attribution: 'Rémi – Centre-Val de Loire', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'car-jaune-reunion', + name: 'Réunion Car Jaune', + operator: 'La Réunion (Car Jaune)', + region: 'La Réunion, France', + center: { + lat: -21.158, + lon: 55.487, + }, + loadRadiusKm: 45, + url: 'https://pysae.com/api/v2/groups/car-jaune/gtfs-rt', + licence: 'lov2', + record: + 'horaires-theoriques-au-format-gtfs-et-horaires-temps-reel-au-format-gtfs-rt-du-reseau-car-jaune-a-la-reunion', + vehicles: 59, + attribution: 'Car Jaune – La Réunion', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'reso-sable-sur-sarthe', + name: 'Sablé-sur-Sarthe Réso', + operator: 'Communauté de communes du Pays Sabolien (Réso)', + region: 'Sablé-sur-Sarthe, France', + center: { + lat: 47.839, + lon: -0.326, + }, + loadRadiusKm: 10, + url: 'https://gtfs-rt.infra-hubup.fr/reso/realtime', + licence: 'odc-odbl', + record: 'reseau-urbain-reso', + vehicles: 2, + attribution: 'Réso – Communauté de communes du Pays Sabolien', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'brevibus-saint-brevin', + name: 'Saint-Brevin Brévibus', + operator: 'Communauté de communes du Sud Estuaire (Brévibus)', + region: 'Saint-Brevin-les-Pins, France', + center: { + lat: 47.242, + lon: -2.157, + }, + loadRadiusKm: 15, + url: 'https://pysae.com/api/v2/groups/brevibus/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-brevibus', + vehicles: 1, + attribution: 'Brévibus – Communauté de communes du Sud Estuaire', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'mat-saint-malo', + name: 'Saint-Malo MAT', + operator: + 'Communauté d’agglomération du Pays de Saint Malo Agglomération (MAT)', + region: 'Saint-Malo, France', + center: { + lat: 48.634, + lon: -1.97, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/mat-st-malo-gtfs-rt', + licence: 'lov2', + record: 'reseau-urbain-mat', + vehicles: 30, + attribution: + 'MAT – Communauté d’agglomération du Pays de Saint Malo Agglomération', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'mouveo-saint-omer', + name: 'Saint-Omer Mouvéo', + operator: 'Communauté d’agglomération du Pays de Saint-Omer (Mouvéo)', + region: 'Saint-Omer, France', + center: { + lat: 50.735, + lon: 2.274, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/mouveo-saint-omer-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'transports-en-commun-audomarois-1', + vehicles: 32, + attribution: 'Mouvéo – Communauté d’agglomération du Pays de Saint-Omer', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'alterneo-reunion', + name: 'Saint-Pierre Alternéo', + operator: + 'Communauté d’agglomération CIVIS (Communauté Intercommunale des Villes Solidaires) (Alternéo)', + region: 'Saint-Pierre, La Réunion, France', + center: { + lat: -21.275, + lon: 55.447, + }, + loadRadiusKm: 20, + url: 'https://proxy.transport.data.gouv.fr/resource/alterneo-civis-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'donnees-du-reseau-alterneo', + vehicles: 17, + attribution: + 'Alternéo – Communauté d’agglomération CIVIS (Communauté Intercommunale des Villes Solidaires)', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'saint-sulpice-la-pointe', + name: 'Saint-Sulpice-la-Pointe buses', + operator: 'Région Occitanie (CC Tarn-Agout) (ST SULPICE LA POINTE)', + region: 'Saint-Sulpice-la-Pointe, France', + center: { + lat: 43.772, + lon: 1.681, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=saint-sulpice-la-pointe', + licence: 'lov2', + record: 'reseau-de-transport-urbain-de-la-ville-de-st-sulpice-la-pointe', + vehicles: 2, + attribution: 'ST SULPICE LA POINTE – Région Occitanie (CC Tarn-Agout)', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'buss-saintes', + name: 'Saintes Buss', + operator: 'Communauté d’agglomération de Saintes (Buss)', + region: 'Saintes, France', + center: { + lat: 45.745, + lon: -0.629, + }, + loadRadiusKm: 20, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=buss-cdasaintes', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-buss-saintes-gtfs-gtfs-rt', + vehicles: 19, + attribution: 'Buss – Communauté d’agglomération de Saintes', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'sarlat-bus', + name: 'Sarlat Bus', + operator: 'Communauté de communes Sarlat-Périgord Noir (Sarlat Bus)', + region: 'Sarlat-la-Canéda, France', + center: { + lat: 44.89, + lon: 1.218, + }, + loadRadiusKm: 10, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=Sarlat', + licence: 'odc-odbl', + record: + 'horaires-theoriques-et-temps-reel-du-reseau-sarlatbus-gtfs-gtfs-rt', + vehicles: 1, + attribution: 'Sarlat Bus – Communauté de communes Sarlat-Périgord Noir', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'illygo-sarthe', + name: 'Sarthe Illygo', + operator: 'Pôle Métropolitain Mobilités Le Mans – Sarthe (Illygo)', + region: 'Sarthe, France', + center: { + lat: 47.985, + lon: 0.234, + }, + loadRadiusKm: 40, + url: 'https://pysae.com/api/v2/groups/illygo/gtfs-rt', + licence: 'odc-odbl', + record: 'reseau-urbain-illygo', + vehicles: 5, + attribution: 'Illygo – Pôle Métropolitain Mobilités Le Mans – Sarthe', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'sam-sete', + name: 'Sète Agglopôle Mobilité', + operator: + 'Communauté d’agglomération Sète Agglopôle Méditerranée (Sète Agglopole Mobilité)', + region: 'Sète, France', + center: { + lat: 43.428, + lon: 3.689, + }, + loadRadiusKm: 20, + url: 'https://sete.ceccli.com/gtfs/VehiclePositions.pb', + licence: 'lov2', + record: 'reseau-bus-sete-agglopole-mobilite', + vehicles: 48, + attribution: + 'Sète Agglopole Mobilité – Communauté d’agglomération Sète Agglopôle Méditerranée', + }, + { + id: 'tlp-tarbes-lourdes', + name: 'Tarbes–Lourdes TLP Mobilités', + operator: 'Communauté d’agglomération Tarbes-Lourdes-Pyrénées (TLP)', + region: 'Tarbes, France', + center: { + lat: 43.228, + lon: 0.065, + }, + loadRadiusKm: 25, + url: 'https://zenbus.net/gtfs/rt/poll.proto?dataset=tlp-mobilites', + licence: 'lov2', + record: 'horaires-theoriques-du-reseau-tarbes-lourdes-tlp-mobilites-gtfs', + vehicles: 24, + attribution: 'TLP – Communauté d’agglomération Tarbes-Lourdes-Pyrénées', + }, + { + id: 'tcat-troyes', + name: 'Troyes TCAT', + operator: 'Communauté d’agglomération Troyes Champagne Métropole (TCAT)', + region: 'Troyes, France', + center: { + lat: 48.292, + lon: 4.072, + }, + loadRadiusKm: 15, + url: 'https://proxy.transport.data.gouv.fr/resource/tcat-troyes-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: 'donnees-tcat-troyes-champagne-metropole-1', + vehicles: 52, + attribution: 'TCAT – Communauté d’agglomération Troyes Champagne Métropole', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'citea-valence', + name: 'Valence–Romans Citéa', + operator: 'Valence-Romans déplacements (Citéa)', + region: 'Valence, France', + center: { + lat: 44.945, + lon: 4.942, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/citea-valence-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'ce-jeu-de-donnees-contient-la-liste-des-arrets-des-horaires-et-des-parcours-theoriques-du-reseau-de-transport-urbain-et-interurbain-de-vrm', + vehicles: 130, + attribution: 'Citéa – Valence-Romans déplacements', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'transvilles-valenciennes', + name: 'Valenciennes Transvilles', + operator: + 'Syndicat intercommunal de mobilité et d’organisation urbaine du Valenciennois (Transvilles)', + region: 'Valenciennes, France', + center: { + lat: 50.367, + lon: 3.49, + }, + loadRadiusKm: 20, + url: 'https://ara-api.enroute.mobi/transvilles/gtfs', + licence: 'lov2', + record: + 'simouv-horaires-theoriques-format-gtfs-et-temps-reel-format-siri-transvilles-valenciennes', + vehicles: 87, + attribution: + 'Transvilles – Syndicat intercommunal de mobilité et d’organisation urbaine du Valenciennois', + routeModes: { + 'NAVINEO:Line::190:LOC': 'tram', + 'NAVINEO:Line::191:LOC': 'tram', + }, + note: 'Lines T1 and T2 (route ids 190 and 191) are the tramway. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'kiceo-vannes', + name: 'Vannes Kicéo', + operator: + 'Communauté d’agglomération Golfe du Morbihan - Vannes Agglomération (Kicéo)', + region: 'Vannes, France', + center: { + lat: 47.661, + lon: -2.756, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/kiceo-vannes-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'reseau-urbain-kiceo', + vehicles: 63, + attribution: + 'Kicéo – Communauté d’agglomération Golfe du Morbihan - Vannes Agglomération', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'move-vendome', + name: 'Vendôme Move', + operator: 'Communauté d’agglomération Territoires Vendômois (Move)', + region: 'Vendôme, France', + center: { + lat: 47.799, + lon: 1.061, + }, + loadRadiusKm: 25, + url: 'https://pysae.com/api/v2/groups/vendome/gtfs-rt', + licence: 'lov2', + record: 'gtfs-move-vendome', + vehicles: 7, + attribution: 'Move – Communauté d’agglomération Territoires Vendômois', + note: 'Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'sngo-vernon', + name: 'Vernon SNgo!', + operator: + 'Communauté d’agglomération Seine Normandie Agglomération (SNgo!)', + region: 'Vernon, France', + center: { + lat: 49.093, + lon: 1.47, + }, + loadRadiusKm: 25, + url: 'https://proxy.transport.data.gouv.fr/resource/sngo-vernon-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'sngo-vernon-les-andelys-seine-normandie-agglo', + vehicles: 7, + attribution: + 'SNgo! – Communauté d’agglomération Seine Normandie Agglomération', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'moova-vesoul', + name: 'Vesoul Moova', + operator: 'Communauté d’agglomération de Vesoul (moova)', + region: 'Vesoul, France', + center: { + lat: 47.627, + lon: 6.145, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/moova-vesoul-gtfs-rt-vehicle-position', + licence: 'lov2', + record: + 'horaires-theoriques-du-reseau-moova-communaute-dagglomeration-de-vesoul-gtfs', + vehicles: 7, + attribution: 'moova – Communauté d’agglomération de Vesoul', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'mobivie-vichy', + name: 'Vichy Mobivie', + operator: 'Communauté d’agglomération Vichy Communauté (Mobivie)', + region: 'Vichy, France', + center: { + lat: 46.127, + lon: 3.426, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/mobivie-vichy-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'mobivie-reseau-urbain-vichy-communaute', + vehicles: 11, + attribution: 'Mobivie – Communauté d’agglomération Vichy Communauté', + note: 'Served by the national access point’s own proxy of the operator feed.', + }, + { + id: 'lva-vienne', + name: 'Vienne L’va', + operator: 'Communauté d’agglomération Vienne Condrieu (L’va)', + region: 'Vienne, France', + center: { + lat: 45.52, + lon: 4.88, + }, + loadRadiusKm: 25, + url: 'https://feed-vienne-mobi.ratpdev.com/GTFS-RT/gtfs-rt.bin', + licence: 'odc-odbl', + record: 'reseau-lva', + vehicles: 44, + attribution: 'L’va – Communauté d’agglomération Vienne Condrieu', + }, + { + id: 'vib-vierzon', + name: 'Vierzon Le Vib’', + operator: 'Région Centre-Val de Loire (CC Vierzon-Sologne-Berry) (Le Vib’)', + region: 'Vierzon, France', + center: { + lat: 47.223, + lon: 2.072, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/le-vib-vierzon-gtfs-rt-vehicle-position', + licence: 'odc-odbl', + record: 'reseau-urbain-le-vib', + vehicles: 4, + attribution: + 'Le Vib’ – Région Centre-Val de Loire (CC Vierzon-Sologne-Berry)', + note: 'Served by the national access point’s own proxy of the operator feed. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'vikibus-yvetot', + name: 'Yvetot Vikibus', + operator: 'Communauté de communes Yvetot Normandie (Vikibus)', + region: 'Yvetot, France', + center: { + lat: 49.617, + lon: 0.759, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/vikibus-yvetot-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'vikibus-yvetot-normandie', + vehicles: 3, + attribution: 'Vikibus – Communauté de communes Yvetot Normandie', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, + { + id: 'lio-occitanie', + name: 'liO Occitanie coaches', + operator: 'Occitanie (liO Occitanie)', + region: 'Occitanie, France', + center: { + lat: 43.606, + lon: 2.426, + }, + loadRadiusKm: 210, + url: 'https://lio.2cloud.app/api/gtfsrt/2.0/vehiclepositions/LIO65-6765-2617-7480/bin', + licence: 'odc-odbl', + record: 'reseau-lio-occitanie', + vehicles: 243, + attribution: 'liO Occitanie – Occitanie', + }, + { + id: 'transurbain-evreux', + name: 'Évreux Transurbain', + operator: + 'Communauté d’agglomération Evreux Portes de Normandie (Transurbain)', + region: 'Évreux, France', + center: { + lat: 49.018, + lon: 1.149, + }, + loadRadiusKm: 10, + url: 'https://proxy.transport.data.gouv.fr/resource/transurbain-evreux-gtfs-rt-vehicle-position', + licence: 'lov2', + record: 'transurbain-evreux-portes-de-normandie', + vehicles: 24, + attribution: + 'Transurbain – Communauté d’agglomération Evreux Portes de Normandie', + note: 'Served by the national access point’s own proxy of the operator feed. Published through the Atoumod (Normandy) hub; its positions stalled for about ten minutes once on 2026-11-13. Where a route id is not the line name, the name comes from `routes.txt` in the same record’s GTFS.', + }, +]; diff --git a/src/data/transitFeedsFrance.test.mjs b/src/data/transitFeedsFrance.test.mjs new file mode 100644 index 000000000..d9cd7e238 --- /dev/null +++ b/src/data/transitFeedsFrance.test.mjs @@ -0,0 +1,201 @@ +import { readFileSync } from 'node:fs'; +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { + TRANSIT_FEED_REGISTRY, + TRANSIT_FEED_ID_PATTERN, + getTransitFeed, + publicTransitCatalog, + transitFeedsInRange, + transitModeFor, + transitModeResolved, + transitRouteLabel, +} from './transitFeeds.js'; +import { FRANCE_TRANSIT_FEED_ROWS } from './transitFeedsFrance.js'; +import { FRANCE_ROUTE_NAMES } from './transitRouteNamesFrance.js'; +import { + TRANSIT_ADMISSION_MAX_GLOBAL, + TRANSIT_ADMISSION_WINDOW_MS, + TRANSIT_PROXY_TTL_MS, +} from './transitProxy.js'; + +const FRANCE_IDS = new Set(FRANCE_TRANSIT_FEED_ROWS.map((row) => row.id)); + +test('every French row becomes a live feed under its record’s open licence', () => { + assert.equal(FRANCE_IDS.size, FRANCE_TRANSIT_FEED_ROWS.length); + for (const row of FRANCE_TRANSIT_FEED_ROWS) { + assert.match(row.id, TRANSIT_FEED_ID_PATTERN); + const feed = getTransitFeed(row.id); + assert.ok(feed, `${row.id} is routable`); + assert.ok( + publicTransitCatalog().some((entry) => entry.id === row.id), + `${row.id} is offered to the browser`, + ); + assert.ok( + ['lov2', 'odc-odbl'].includes(row.licence), + `${row.id} states an open licence`, + ); + assert.equal( + feed.license, + row.licence === 'lov2' ? 'Licence Ouverte 2.0' : 'ODbL', + ); + assert.equal( + feed.licenseUrl, + `https://transport.data.gouv.fr/datasets/${row.record}`, + `${row.id} links its dataset record`, + ); + assert.equal(feed.terms.quote, `licence: ${row.licence}`); + assert.match( + feed.terms.note, + /\(\d+ vehicles on 2026-11-13\)/, + `${row.id} records the day it was seen live`, + ); + assert.ok(row.vehicles > 0, `${row.id} had vehicles when it was added`); + assert.ok( + transitFeedsInRange(row.center.lat, row.center.lon).some( + (entry) => entry.id === row.id, + ), + `${row.id} covers its own centre`, + ); + } +}); + +test('no French row repeats a feed that is already registered', () => { + const seen = new Map(); + for (const feed of TRANSIT_FEED_REGISTRY) { + const url = new URL(feed.url); + const key = `${url.origin}${url.pathname.replace(/\/$/, '')}${url.search}`; + assert.equal( + seen.has(key), + false, + `${feed.id} and ${seen.get(key)} fetch the same upstream`, + ); + seen.set(key, feed.id); + } + // The networks that were already here through the operator's own endpoint + // stay single: no French row points at a dataset record one of them cites. + const earlier = TRANSIT_FEED_REGISTRY.filter( + (feed) => !FRANCE_IDS.has(feed.id), + ); + for (const id of [ + 'tbm-bordeaux', + 'tam-montpellier', + 'astuce-rouen', + 'mpm-toulon', + 'lia-le-havre', + 'filbleu-tours', + ]) { + assert.ok(earlier.some((feed) => feed.id === id)); + } + const earlierRecords = new Set(earlier.map((feed) => feed.licenseUrl)); + for (const row of FRANCE_TRANSIT_FEED_ROWS) { + assert.equal( + earlierRecords.has(getTransitFeed(row.id).licenseUrl), + false, + `${row.id} is not a second copy of an earlier feed`, + ); + } +}); + +test('one viewpoint never asks for more feeds than the proxy admits', () => { + // The browser polls each in-range feed once per proxy TTL, and the proxy + // admits a fixed number of upstream requests a minute across all feeds. + const pollsPerFeed = TRANSIT_ADMISSION_WINDOW_MS / TRANSIT_PROXY_TTL_MS; + for (const feed of TRANSIT_FEED_REGISTRY) { + const inRange = transitFeedsInRange(feed.center.lat, feed.center.lon); + assert.ok( + inRange.length * pollsPerFeed <= TRANSIT_ADMISSION_MAX_GLOBAL, + `${inRange.length} feeds overlap at ${feed.id}`, + ); + } +}); + +test('French trams and boats take their mode; every other route stays a default', () => { + const cases = [ + ['divia-dijon', '4-T1', 'tram'], + ['bibus-brest', 'A', 'tram'], + ['setram-le-mans', 'T2', 'tram'], + ['lignes-dazur-nice', 'L2', 'tram'], + ['irigo-angers', 'C', 'tram'], + ['orizo-avignon', 'T1', 'tram'], + ['transvilles-valenciennes', 'NAVINEO:Line::190:LOC', 'tram'], + ['aleop-pays-de-la-loire', 'YEU', 'ferry'], + ['martinique-sud', '4001', 'ferry'], + ]; + for (const [id, routeId, mode] of cases) { + const feed = getTransitFeed(id); + assert.equal(transitModeFor(feed, routeId), mode, `${id} ${routeId}`); + assert.equal(transitModeResolved(feed, routeId), true); + assert.equal(transitModeFor(feed, 'no-such-route'), 'bus'); + assert.equal( + transitModeResolved(feed, 'no-such-route'), + false, + `${id} defaults rather than resolves an unlisted route`, + ); + } + const busOnly = getTransitFeed('ametis-amiens'); + assert.equal(transitModeFor(busOnly, '7'), 'bus'); + assert.equal(transitModeResolved(busOnly, '7'), false); +}); + +test('French feeds that publish km/h are marked for conversion, and only those', () => { + assert.deepEqual( + FRANCE_TRANSIT_FEED_ROWS.filter((row) => row.speedUnit === 'km/h') + .map((row) => row.id) + .sort(), + [ + 'azalys-blois', + 'divia-dijon', + 'maelis-montlucon', + 'rubis-bourg-en-bresse', + 'setram-le-mans', + ], + ); + for (const row of FRANCE_TRANSIT_FEED_ROWS.filter((r) => r.speedUnit)) { + assert.match(getTransitFeed(row.id).terms.note, /Speeds are km\/h/); + } +}); + +test('French route-name tables belong to registered feeds and never blank a line', () => { + for (const [id, names] of Object.entries(FRANCE_ROUTE_NAMES)) { + assert.ok(FRANCE_IDS.has(id), `${id} is a registered French feed`); + const feed = getTransitFeed(id); + for (const [routeId, name] of Object.entries(names)) { + assert.ok(typeof name === 'string' && name.trim() === name && name); + assert.notEqual(name, routeId, `${id} ${routeId} is not a no-op`); + assert.equal(transitRouteLabel(feed, routeId), name); + } + assert.equal(transitRouteLabel(feed, 'no-such-route'), 'no-such-route'); + assert.equal(transitRouteLabel(feed, null), null); + } + assert.equal(transitRouteLabel(getTransitFeed('divia-dijon'), '4-T1'), 'T1'); + assert.equal( + transitRouteLabel( + getTransitFeed('transvilles-valenciennes'), + 'NAVINEO:Line::190:LOC', + ), + 'T1', + ); + // A feed whose ids already read as the line has no table and is left alone. + assert.equal(transitRouteLabel(getTransitFeed('ametis-amiens'), '7'), '7'); +}); + +test('every French feed is documented with its licence', () => { + const lines = readFileSync( + new URL('../../DATA_SOURCES.md', import.meta.url), + 'utf8', + ).split('\n'); + for (const row of FRANCE_TRANSIT_FEED_ROWS) { + const feed = getTransitFeed(row.id); + const line = lines.find((l) => l.startsWith(`| **${feed.name}** `)); + assert.ok(line, `${feed.name} has a DATA_SOURCES row`); + assert.ok( + line.includes(feed.licenseUrl), + `${feed.name} row links its record`, + ); + assert.ok( + line.includes(feed.license), + `${feed.name} row names its licence`, + ); + } +}); diff --git a/src/data/transitRouteNamesFrance.js b/src/data/transitRouteNamesFrance.js new file mode 100644 index 000000000..65acd82d3 --- /dev/null +++ b/src/data/transitRouteNamesFrance.js @@ -0,0 +1,1402 @@ +/** + * French route ids to the line a rider would say, by feed id. + * + * These feeds send the operator's internal route keys (`1020` is Alternéo + * line 2, `zenbus:Line:604130034:LOC` is Cap'Bus line 5). Each table is + * copied from `routes.txt` (`route_id` to `route_short_name`) in the GTFS of + * the feed's own transport.data.gouv.fr record, read 2026-11-13, under that + * record's licence. An id missing here renders as its raw key. + */ +export const FRANCE_ROUTE_NAMES = { + 'capbus-agde': { + 'zenbus:Line:580030006:LOC': '7', + 'zenbus:Line:604130034:LOC': '5', + 'zenbus:Line:613520001:LOC': '3', + 'zenbus:Line:623010001:LOC': '2', + 'zenbus:Line:6336924114485248:LOC': '1', + 'zenbus:Line:719730003:LOC': '4', + }, + 'aleop-pays-de-la-loire': { + '07': 'L7', + '101-AD': 'R101', + '101E': 'E101', + '102-AD': 'R102', + '102E': 'E102', + '103-AD': 'R103', + '104-AD': 'R104', + '105-AD': 'R105', + '107-AD': 'R107', + '108-AD': 'R108', + '109-AD': 'R109', + '109E': 'E109', + '110-AD': 'R110', + '111-AD': 'R111', + '112-AD': 'R112', + '113-AD': 'R113', + '116-AD': 'R116', + '118-AD': 'R118', + 12: 'L12', + 13: 'L13', + '140-AD': 'R140', + '140E': 'E140', + '145E': 'E145', + 15: 'L15', + '150-AD': 'R150', + 16: 'L16', + 17: 'L17', + '212_E': 'E212', + '212_S': '212', + '216_E': 'E216', + '216_S': '216', + 26: 'L26', + '305AT': '305', + 309: 'E309', + 311: 'E311', + 313: 'E313', + 'T5 ESAT': 'ESAT', + }, + 'irigo-angers': { + 20: 'E20', + 21: 'E21', + 22: 'E22', + 23: 'E23', + 24: 'E24', + 25: 'E25', + }, + 'envia-arles': { + 10: '114', + 11: '101', + 12: '105', + 13: '106', + 14: '102', + 15: '103', + 16: '104', + 17: '107', + 21: '109', + 22: '108', + 23: '112', + 31: 'A10', + 32: 'A20', + 33: 'A30', + 34: 'A40', + 35: 'A50', + 38: '1', + 39: '12', + 40: '2', + 41: '3', + 42: '4', + 43: '5', + 45: 'T', + 5: '110', + 6: '111', + 73: '113', + 75: '11', + 77: '116', + 9: '115', + }, + 'artis-arras': { + '1-26': '1', + '10-26': '10', + '11-26': '11', + '12-26': '12', + '13-26': '13', + '14-26': '14', + '15-26': '15', + '16-26': '16', + '17-26': '17', + '18-26': '18', + '2-26': '2', + '3-26': '3', + '4-26': '4', + '5-26': '5', + '6-26': '6', + '7-26': '7', + '8-26': '8', + '9-26': '9', + 'CIT1-26': 'CIT1', + 'CIT2-26': 'CIT2', + 'CIT3-26': 'CIT3', + 'D1-26': 'D1', + 'D2-26': 'D2', + 'S1-26': 'S1', + 'S10-26': 'S10', + 'S11-26': 'S11', + 'S12-26': 'S12', + 'S13-26': 'S13', + 'S14-26': 'S14', + 'S15-26': 'S15', + 'S16-26': 'S16', + 'S17-26': 'S17', + 'S18-26': 'S18', + 'S19-26': 'S19', + 'S2-26': 'S2', + 'S20-26': 'S20', + 'S21-26': 'S21', + 'S22-26': 'S22', + 'S23-26': 'S23', + 'S24-26': 'S24', + 'S25-26': 'S25', + 'S26-26': 'S26', + 'S27-26': 'S27', + 'S3-26': 'S3', + 'S4-26': 'S4', + 'S5-26': 'S5', + 'S6-26': 'S6', + 'S7-26': 'S7', + 'S8-26': 'S8', + 'S9-26': 'S9', + 'ZA1-26': 'ZA1', + 'ZA2-26': 'ZA2', + 'ZA3-26': 'ZA3', + }, + 'toutenbus-aubenas': { + 4401: 'L01', + 4402: 'L02', + 4403: 'L03', + 4404: 'L04', + 4406: 'L06', + 4421: 'L21', + 4422: 'L22', + 4423: 'L23', + 4425: 'L25', + 4451: 'L02exL', + 4452: 'L02exS', + 4464: 'L64', + 4465: 'L65', + 4466: 'L66', + 4467: 'L67', + 4468: 'L68', + 4469: 'L69', + 4473: 'X73', + 4474: 'X74', + 4481: 'L01ex', + 4483: 'L03ex', + }, + 'alliance-auch': { + 'zenbus:Line:301300003:LOC': 'A', + 'zenbus:Line:30780001:LOC': 'D', + 'zenbus:Line:311070002:LOC': 'E', + 'zenbus:Line:320990001:LOC': 'B', + 'zenbus:Line:324880001:LOC': 'F', + 'zenbus:Line:38010061:LOC': 'C', + 'zenbus:Line:624560003:LOC': 'AUSC', + 'zenbus:Line:632620001:LOC': 'G', + }, + 'glazgo-auray': { + 13: 'A', + 14: 'B', + 15: 'C', + }, + 'lbus-bernay': { + IBUS_2: 'IBUS', + IBUS_3: 'IBUS', + }, + 'azalys-blois': { + ADA1: 'ADA 1', + ADA2: 'ADA 2', + L161: 'L16-1', + L162: 'L16-2', + L231: 'L23-1', + L232: 'L23-2', + RE16: 'Rém16', + S601: 'S60-1', + S602: 'S60-2', + S671: 'S67-1', + S672: 'S67-2', + S681: 'S68-1', + S682: 'S68-2', + }, + 'palmbus-cannes': { + 281: 'SOPHIA', + 8: 'IMP', + }, + 'cmonbus-cavaillon': { + 'Ligne 4': '4', + 'Navette centre ville': 'NCV1', + 'zenbus:Line:550340001:LOC': 'B', + 'zenbus:Line:550350001:LOC': '1', + 'zenbus:Line:554300001:LOC': '3', + 'zenbus:Line:556270001:LOC': 'C', + 'zenbus:Line:560520001:LOC': '2', + 'zenbus:Line:565580001:LOC': 'CMNA', + 'zenbus:Line:575240001:LOC': 'A', + 'zenbus:Line:578970001:LOC': 'D', + 'zenbus:Line:892310003:LOC': 'E', + }, + 'papillon-cosne': { + centre_ville: 'L1', + hameaux: 'L2', + }, + 'taco-coeur-du-var': { + 335: 'TACO', + }, + 'deepmob-dieppe': { + 'ATOUMOD005:Line:100:LOC': '100', + 'ATOUMOD005:Line:14:LOC': '14', + 'ATOUMOD005:Line:15:LOC': '15', + 'ATOUMOD005:Line:1:LOC': '1', + 'ATOUMOD005:Line:2:LOC': '2', + 'ATOUMOD005:Line:3:LOC': '3', + 'ATOUMOD005:Line:400:LOC': '400', + 'ATOUMOD005:Line:500:LOC': '500', + 'ATOUMOD005:Line:600:LOC': '600', + 'ATOUMOD005:Line:700:LOC': '700', + 'ATOUMOD005:Line:900:LOC': '900', + 'ATOUMOD005:Line:97:LOC': 'N', + 'ATOUMOD005:Line:TRANSMANCHE:LOC': 'T', + }, + 'divia-dijon': { + '4-10': '10', + '4-12': '12', + '4-13': '13', + '4-14': '14', + '4-15': '15', + '4-16': '16', + '4-18': '18', + '4-19': '19', + '4-30': '30', + '4-31': '31', + '4-32': '32', + '4-33': '33', + '4-34': '34', + '4-35': '35', + '4-36': '36', + '4-37': '37', + '4-39': '39', + '4-40': '40', + '4-41': '41', + '4-42': '42', + '4-43': '43', + '4-44': '44', + '4-45': '45', + '4-46': '46', + '4-51': '51', + '4-61': '61', + '4-62': '62', + '4-63': '63', + '4-64': '64', + '4-65': '65', + '4-66': '66', + '4-67': '67', + '4-68': '68', + '4-69': '69', + '4-70': '70', + '4-71': '71', + '4-72': '72', + '4-73': '73', + '4-74': '74', + '4-75': '75', + '4-83': '83', + '4-93': '93', + '4-98': '98', + '4-99': '99', + '4-CITY': 'CITY', + '4-CO': 'CO', + '4-FNI': 'FNI', + '4-L3': 'L3', + '4-L4': 'L4', + '4-L5': 'L5', + '4-L6': 'L6', + '4-L7': 'L7', + '4-L8': 'L8', + '4-L9': 'L9', + '4-LM': 'LM', + '4-NAVC': 'NAVC', + '4-PB': 'PB', + '4-PL': 'PL', + '4-T1': 'T1', + '4-T2': 'T2', + }, + 'tedbus-draguignan': { + 1000001: '1', + 1000002: '10', + 1000003: '11', + 1000004: '13', + 1000005: '2', + 1000006: '3', + 1000007: '5', + 1000008: '6', + 1000009: '9', + 1000321: '305-L5', + 1000322: '302-L5', + 1000323: '306-L5', + 1000324: '413-L5', + 1000341: '12', + }, + 'lebus-figeac': { + 'zenbus:Line:350500015:LOC': 'L7', + 'zenbus:Line:350510003:LOC': 'L9', + 'zenbus:Line:352570004:LOC': 'L6', + 'zenbus:Line:358700005:LOC': 'L1', + 'zenbus:Line:364620005:LOC': 'L10', + 'zenbus:Line:366310002:LOC': 'L12', + 'zenbus:Line:371990011:LOC': 'L2', + 'zenbus:Line:375020010:LOC': 'L4', + 'zenbus:Line:376920006:LOC': 'L8', + 'zenbus:Line:376930002:LOC': 'L5', + 'zenbus:Line:380800004:LOC': 'L3', + 'zenbus:Line:4896114768609280:LOC': 'L9 Bis', + 'zenbus:Line:5204162090369024:LOC': 'NAV', + }, + 'nemus-flers': { + 219: 'URB1', + 220: 'URB2', + 221: 'URB3', + }, + 'surf-fougeres': { + 41: '1', + 42: '2', + 43: '3', + 44: '4', + 45: '5', + 46: '6', + 58: 'Navette Noel', + 60: '7', + }, + 'lebus-frejus': { + Boulouris: 'Boulou', + Capitou: 'Capito', + Esterel: 'Estere', + Roquebrune: 'Roqueb', + }, + 'lila-presquile-guerande': { + 144: '1', + 145: '10', + 146: '11', + 147: '12', + 148: '13', + 149: '14', + 150: '15', + 151: '16', + 152: '17', + 153: '18', + 154: '19', + 155: '2', + 156: '20', + 157: '23', + 158: '24L', + 159: '25', + 160: '27', + 161: '3', + 162: '30', + 163: '31', + 165: '33', + 166: '34', + 167: '35', + 168: '37', + 169: '38', + 17: '4', + 170: '39', + 171: '4', + 173: '41', + 174: '42', + 175: '43', + 176: '46', + 177: '47', + 178: '49', + 179: '5L', + 18: '5', + 180: '50', + 181: '51', + 182: '52', + 183: '53', + 184: '54', + 185: '55', + 186: '56', + 187: '6L', + 188: '60L', + 189: '61L', + 190: '62L', + 191: '63', + 192: '64L', + 193: '65L', + 194: '66', + 195: '67', + 196: '68L', + 197: '69', + 198: '7L', + 199: '70', + 2: '10', + 200: '71', + 201: '72L', + 202: '73L', + 203: '74', + 204: '75L', + 205: '76L', + 206: '77', + 208: '82', + 210: '9L', + 213: 'F1C', + 214: 'F2C', + 215: 'F3C', + 216: 'F4C', + 217: 'F5', + 218: 'H1C', + 219: 'H2C', + 220: 'H3C', + 221: 'H4C', + 222: 'H5', + 223: 'H6C', + 224: 'H7C', + 225: 'H8C', + 226: 'H9', + 255: '20Ouest', + 256: '20Est', + 257: 'Navette A', + 258: 'Navette B', + 259: 'Navette C', + 26: '6', + 260: '98', + 261: '99', + 265: '5C', + 266: '6C', + 267: '7C', + 268: '9C', + 269: '24C', + 270: '60P', + 271: '61P', + 272: '62P', + 273: '64P', + 274: '65P', + 275: '68P', + 276: '72P', + 277: '73P', + 278: '75P', + 279: '76P', + 28: '7', + 280: 'F1P', + 281: 'F2P', + 282: 'F3P', + 283: 'F4P', + 284: 'H1P', + 285: 'H2P', + 286: 'H3P', + 287: 'H4P', + 288: 'H6P', + 289: 'H7P', + 290: 'H8P', + 3: '13', + 30: '8', + 33: '9', + 34: 'A1', + 4: '2', + }, + 'ritmo-haguenau': { + 'zenbus:Line:726780019:LOC': '05', + 'zenbus:Line:736370020:LOC': '13', + 'zenbus:Line:737930001:LOC': '01', + 'zenbus:Line:743630001:LOC': '11', + 'zenbus:Line:747400002:LOC': '03', + 'zenbus:Line:753020003:LOC': '12', + 'zenbus:Line:756890009:LOC': '02', + 'zenbus:Line:758860001:LOC': '10', + 'zenbus:Line:760800001:LOC': '04', + }, + 'hopbus-hazebrouck': { + 209: 'L 1', + 210: 'L 2', + 211: 'L 3', + 212: 'NB', + 213: 'NH1', + 214: 'NH2', + 215: 'NN', + 216: 'HDA', + 217: 'HDB.', + 218: 'HDA.', + }, + 'hobus-honfleur': { + 'ATOUMOD021:Line:A:LOC': 'A', + 'ATOUMOD021:Line:B:LOC': 'B', + 'ATOUMOD021:Line:C:LOC': 'C', + 'ATOUMOD021:Line:N:LOC': 'N', + }, + 'cherpa-issoire': { + APILA_5: 'APILA', + }, + 'lisatis-lavaur': { + 'zenbus:Line:598580001:LOC': 'L1', + 'zenbus:Line:600180025:LOC': 'T', + 'zenbus:Line:602270023:LOC': 'L2', + }, + 'astrobus-lisieux': { + 'ATOUMOD036:Line:4558066457509888:LOC': '39', + 'ATOUMOD036:Line:4612030137892864:LOC': '27', + 'ATOUMOD036:Line:4666911632654336:LOC': '45', + 'ATOUMOD036:Line:4683467422957568:LOC': '42', + 'ATOUMOD036:Line:4743889022877696:LOC': '28', + 'ATOUMOD036:Line:4824043984257024:LOC': '41', + 'ATOUMOD036:Line:4826873981829120:LOC': '15', + 'ATOUMOD036:Line:4841323284660224:LOC': '3', + 'ATOUMOD036:Line:4860060908912640:LOC': '21', + 'ATOUMOD036:Line:4947799775903744:LOC': '20', + 'ATOUMOD036:Line:5089173048393728:LOC': '2', + 'ATOUMOD036:Line:5089804213551104:LOC': '6', + 'ATOUMOD036:Line:5118765622624256:LOC': '5', + 'ATOUMOD036:Line:5126752804274176:LOC': '13', + 'ATOUMOD036:Line:5131776580124672:LOC': '1', + 'ATOUMOD036:Line:5182404740251648:LOC': '4', + 'ATOUMOD036:Line:5205031542325248:LOC': '11', + 'ATOUMOD036:Line:5289643101650944:LOC': '25', + 'ATOUMOD036:Line:5291806154555392:LOC': '32', + 'ATOUMOD036:Line:5349681140858880:LOC': '33', + 'ATOUMOD036:Line:5354718734843904:LOC': '34', + 'ATOUMOD036:Line:5381620933591040:LOC': '26', + 'ATOUMOD036:Line:5805314927493120:LOC': '23', + 'ATOUMOD036:Line:5844004563320832:LOC': '38', + 'ATOUMOD036:Line:5854756107976704:LOC': '35', + 'ATOUMOD036:Line:5878113582972928:LOC': '44', + 'ATOUMOD036:Line:5994644367212544:LOC': '43', + 'ATOUMOD036:Line:6034535755022336:LOC': '22', + 'ATOUMOD036:Line:6106210437693440:LOC': '29', + 'ATOUMOD036:Line:6325671926169600:LOC': '14', + 'ATOUMOD036:Line:6499174041255936:LOC': '37', + 'ATOUMOD036:Line:6556147453526016:LOC': '40', + 'ATOUMOD036:Line:6580502401122304:LOC': '24', + 'ATOUMOD036:Line:6592836607672320:LOC': '30', + }, + 'tgl-longwy': { + 'CMW1-BIS': 'CMW1 Bis', + 'ECOLE-CHENIERES-LAIX': 'Ecole chenieres laix', + }, + 'luneo-luneville': { + 33: 'L1A', + 34: 'L1B', + 35: 'L2', + }, + 'maybus-mayenne': { + 'zenbus:Line:345650009:LOC': 'Bras', + 'zenbus:Line:353560012:LOC': 'Nav', + 'zenbus:Line:367290011:LOC': 'GN', + }, + 'tum-mende': { + 'zenbus:Line:16050162:LOC': '3', + 'zenbus:Line:4549308700426240:LOC': '2', + 'zenbus:Line:4580583746306048:LOC': 'SCO1', + 'zenbus:Line:4606557863018496:LOC': 'SCO3', + 'zenbus:Line:4771309419495424:LOC': '1', + 'zenbus:Line:6033597388554240:LOC': 'SCO2', + }, + 'lemet-metz': { + 1: 'L1', + 101: 'P101', + 102: 'P102', + 103: 'P103', + 105: 'P105', + 106: 'P106', + 107: 'P107', + 108: 'P108', + 110: 'P110', + 111: 'P111', + 112: 'P112', + 113: 'P113', + 12: 'C12', + 13: 'C13', + 14: 'C14', + 15: 'C15', + 16: 'C16', + 17: 'C17', + 18: 'N18', + 19: 'N19', + 2: 'L2', + 200: 'S200', + 201: 'S201', + 202: 'S202', + 203: 'S203', + 204: 'S204', + 205: 'S205', + 206: 'S206', + 207: 'S207', + 208: 'S208', + 212: 'S212', + 213: 'S213', + 272: 'S272', + 273: 'S273', + 278: 'S278', + 281: 'S281', + 282: 'S282', + 283: 'S283', + 284: 'S284', + 285: 'S285', + 286: 'S286', + 287: 'S287', + 288: 'S288', + 289: 'S289', + 290: 'S290', + 291: 'S291', + 292: 'S292', + 296: 'S296', + 297: 'S297', + 299: 'S299', + 3: 'L3', + 4: 'L4', + 5: 'L5', + 6: 'L6', + 70: 'N70', + 71: 'N71', + 81: 'N81', + 82: 'N82', + 83: 'N83', + '84a': 'N84a', + '84b': 'N84b', + 85: 'N85', + 86: 'N86', + 87: 'N87', + 88: 'N88', + 89: 'N89', + 90: 'N90', + 92: 'N92', + 93: 'N93', + A: 'MA', + B: 'MB', + }, + 'citibus-narbonne': { + S01: 'BC01', + S02: 'BC02', + S03: 'BC03', + S05: 'BC05', + S06: 'BC06', + S07: 'BC07', + S101: 'BC101', + S121: 'BC121', + S122: 'BC122', + S123: 'BC123', + S131: 'BC131', + S132: 'BC132', + S141: 'BC141', + S142: 'BC142', + S143: 'BC143', + S151: 'BC151', + S152: 'BC152', + S153: 'BC153', + S154: 'BC154', + S155: 'BC155', + S161: 'BC161', + S171: 'BC171', + S172: 'BC172', + S173: 'BC173', + S181: 'BC181', + S182: 'BC182', + S183: 'BC183', + S200: 'BC200', + S201: 'BC201', + S202: 'BC202', + S204: 'BC204', + S205: 'BC205', + S206: 'BC206', + S208: 'BC208', + S209: 'BC209', + S210: 'BC210', + S27A: 'BC207A', + S27B: 'BC207B', + S300: 'BC300', + S301: 'BC301', + S302: 'BC302', + S303: 'BC303', + S304: 'BC304', + S305: 'BC305', + S306: 'BC306', + S307: 'BC307', + S309: 'BC309', + S310: 'BC310', + S400: 'BC400', + S401: 'BC401', + S402: 'BC402', + S403: 'BC403', + S404: 'BC404', + S405: 'BC405', + S406: 'BC406', + S407: 'BC407', + S408: 'BC408', + }, + 'lignes-dazur-nice': { + '08': '8+', + 12: '12+', + }, + 'nosbus-nogent-le-rotrou': { + 288: '1', + 289: '2', + 290: 'S', + }, + 'navette-oloron': { + 'zenbus:Line:5073745767038976:LOC': 'A', + 'zenbus:Line:5129214934122496:LOC': 'D', + 'zenbus:Line:5172091324203008:LOC': 'B', + 'zenbus:Line:518340002:LOC': '1', + 'zenbus:Line:5190050797060096:LOC': 'C', + 'zenbus:Line:525740001:LOC': '2', + 'zenbus:Line:547050001:LOC': '3', + }, + 'transpor-pays-de-lor': { + 10: 'C', + 7: 'L3', + 8: 'A', + 9: 'B', + LIGNE_4: 'L4', + LIGNE_ESTIVALE: 'Navette estivale', + }, + 'pondibus-pontivy': { + 283: '1', + 284: '2', + 285: '3', + 286: '4', + 287: '5', + }, + 'a-citadina-porto-vecchio': { + 'MARINA D': 'MAR D', + PRUNEDDU: 'PRU', + 'PRUNEDDU D': 'PRU D', + }, + 'qub-quimper': { + QCIT: 'QUB CITY', + }, + 'titus-rosny-sous-bois': { + 'zenbus:Line:12010117:LOC': 'L1', + 'zenbus:Line:22010107:LOC': 'L2', + }, + 'remi-centre-val-de-loire': { + 'REMI28:Line:1': '1', + 'REMI28:Line:14': '14', + 'REMI28:Line:161': '8', + 'REMI28:Line:18': '15', + 'REMI28:Line:2': '10', + 'REMI28:Line:202': '86', + 'REMI28:Line:207': '87', + 'REMI28:Line:210': '9', + 'REMI28:Line:287': 'M7', + 'REMI28:Line:29': '15A', + 'REMI28:Line:3': '11', + 'REMI28:Line:31': '16', + 'REMI28:Line:33': '161', + 'REMI28:Line:38': '17', + 'REMI28:Line:41': '19', + 'REMI28:Line:43': '1 bis', + 'REMI28:Line:44': '2', + 'REMI28:Line:45': '20', + 'REMI28:Line:51': '21', + 'REMI28:Line:55': '22', + 'REMI28:Line:57': '23', + 'REMI28:Line:66': '25', + 'REMI28:Line:67': '3', + 'REMI28:Line:76': '4', + 'REMI28:Line:87': '419', + 'REMI28:Line:89': '434', + 'REMI28:Line:90': '44', + 'REMI36:Line:433': '433', + 'REMI36:Line:800': '800', + 'REMI36:Line:A': 'A', + 'REMI36:Line:B': 'B', + 'REMI36:Line:F': 'F', + 'REMI36:Line:I': 'I', + 'REMI36:Line:L': 'L', + 'REMI36:Line:Q': 'Q', + 'REMI36:Line:S': 'S', + 'REMI36:Line:U': 'U', + 'REMI36:Line:V': 'V', + 'REMI37:Line:139': 'G', + 'REMI37:Line:140': 'H', + 'REMI37:Line:143': 'H2', + 'REMI37:Line:144': 'I', + 'REMI37:Line:145': 'IV', + 'REMI37:Line:152': 'M', + 'REMI37:Line:156': 'N1', + 'REMI37:Line:157': 'N2', + 'REMI37:Line:29': 'A', + 'REMI37:Line:303': 'P', + 'REMI37:Line:304': 'R', + 'REMI37:Line:306': 'S', + 'REMI37:Line:343': 'TA', + 'REMI37:Line:345': 'TC', + 'REMI37:Line:347': 'TD', + 'REMI37:Line:349': 'TE', + 'REMI37:Line:351': 'TF', + 'REMI37:Line:355': 'TB', + 'REMI37:Line:357': 'TH', + 'REMI37:Line:37': 'C', + 'REMI37:Line:384': 'J', + 'REMI37:Line:389': 'L1', + 'REMI37:Line:390': 'L2', + 'REMI37:Line:59': 'D', + 'REMI37:Line:63': 'F', + 'REMI41:Line:1': '1', + 'REMI41:Line:10': '10', + 'REMI41:Line:11': '11', + 'REMI41:Line:13': '13', + 'REMI41:Line:14': '14', + 'REMI41:Line:16': '16', + 'REMI41:Line:2': '2', + 'REMI41:Line:20': '20', + 'REMI41:Line:3': '3', + 'REMI41:Line:4': '4', + 'REMI41:Line:407': 'CFA', + 'REMI41:Line:429': '429', + 'REMI41:Line:430': '430', + 'REMI41:Line:431': '431', + 'REMI41:Line:440': '440', + 'REMI41:Line:492': '492', + 'REMI41:Line:5': '5', + 'REMI41:Line:51': 'NAVA', + 'REMI41:Line:52': '52', + 'REMI41:Line:53': '53', + 'REMI41:Line:54': '54', + 'REMI41:Line:55': '55', + 'REMI41:Line:6': '6', + 'REMI41:Line:7': '7', + 'REMI41:Line:8': '8', + 'REMI41:Line:9': '9', + 'REMI41:Line:931': '931', + 'REMI41:Line:941': '941', + 'REMI45:Line:103': '6', + 'REMI45:Line:105': '7A', + 'REMI45:Line:106': '8', + 'REMI45:Line:108': '911', + 'REMI45:Line:126': '3C', + 'REMI45:Line:13': '17', + 'REMI45:Line:14': '1A', + 'REMI45:Line:15': '1B', + 'REMI45:Line:16': '1D', + 'REMI45:Line:17': '20A', + 'REMI45:Line:18': '21', + 'REMI45:Line:19': '22', + 'REMI45:Line:2': '11', + 'REMI45:Line:20': '23', + 'REMI45:Line:21': '25', + 'REMI45:Line:3': '12', + 'REMI45:Line:32': '3A', + 'REMI45:Line:33': '3B', + 'REMI45:Line:34': '4', + 'REMI45:Line:36': '417', + 'REMI45:Line:37': '437', + 'REMI45:Line:4': '13', + 'REMI45:Line:5': '14', + 'REMI45:Line:560': '20B', + 'REMI45:Line:580': '9A', + 'REMI45:Line:581': '9B', + 'REMI45:Line:585': '1C', + 'REMI45:Line:586': '24', + 'REMI45:Line:587': '7B', + 'REMI45:Line:588': '10', + 'REMI45:Line:589': '15', + 'REMI45:Line:590': '18', + 'REMI45:Line:6': '16', + 'REMI45:Line:77': 'TAD 45-30', + 'REMI45:Line:78': 'TAD 45-31', + 'REMI45:Line:79': 'TAD 45-32', + 'REMI45:Line:80': 'TAD 45-33', + 'REMI45:Line:81': 'TAD 45-34', + 'REMI45:Line:82': 'TAD 45-35', + 'REMI45:Line:83': 'TAD 45-36', + 'REMI45:Line:84': 'TAD 45-37', + 'REMI45:Line:85': 'TAD 45-38', + 'REMI45:Line:86': 'TAD 45-39', + 'REMI45:Line:87': 'TAD 45-40', + 'REMI45:Line:88': 'TAD 45-41', + 'REMI45:Line:89': 'TAD 45-42', + 'REMI45:Line:90': 'TAD 45-43', + 'REMI45:Line:91': 'TAD 45-44', + 'REMI45:Line:92': 'TAD 45-45', + 'REMI45:Line:93': 'TAD 45-46', + 'REMI45:Line:94': '5', + 'REMI_18:Line:100': '100', + 'REMI_18:Line:105': '105', + 'REMI_18:Line:110': '110', + 'REMI_18:Line:120': '120', + 'REMI_18:Line:145': '145', + 'REMI_18:Line:150': '150', + 'REMI_18:Line:160': '160', + 'REMI_18:Line:185': '185', + 'REMI_18:Line:190': '190', + 'REMI_18:Line:235': '235', + 'REMI_18:Line:300': '300', + }, + 'car-jaune-reunion': { + 11: 'O2', + 13: 'O3', + 16: 'S1', + 18: 'S2', + 2: 'E1', + 20: 'S3', + 22: 'S4', + 24: 'S5', + 26: 'S6', + 28: 'T', + 34: 'E1X', + 35: 'O1X', + 36: 'TX', + 4: 'E2', + 6: 'E3', + 9: 'O1', + }, + 'reso-sable-sur-sarthe': { + 305: 'Navette Express', + 312: 'Renfort scolaire n°1', + 313: 'Ligne régulière', + 954: 'Renfort scolaire n°2', + }, + 'brevibus-saint-brevin': { + 92: '2', + 93: '3', + 96: '1', + }, + 'alterneo-reunion': { + '0990': 'SVB', + 1000: 'GDR', + 1001: 'GDR', + 1002: 'GDR', + 1003: 'SAK', + 1004: 'SAK', + 1005: 'SAK', + 1006: 'GDR', + 1007: 'ODY', + 1008: '2e', + 1009: 'SS', + 1010: '1', + 1020: '2', + 1021: '2EX', + 1030: '3', + 1040: '4', + 1050: '5', + 1060: '6', + 1070: '7', + 1080: '8', + 1090: '9', + 1100: '10', + 1110: '11', + 1120: '12', + 1130: '13', + 1131: '13A', + 1140: '14', + 1150: '15', + 1160: 'KV1', + 1170: 'KV2', + 1181: 'Lit', + 1182: 'LI1', + 1183: 'LI2', + 1184: 'LEx', + 2010: 'KSL', + 2210: '21', + 2220: '22', + 2230: '23', + 2240: '24', + 2250: '25', + 2260: '26', + 2270: '27', + 2280: '28', + 2290: '29', + 2300: '30', + 2310: '31', + 2320: '32', + 2330: '33', + 2340: '34', + 4410: '41', + 4420: '42', + 4430: '43', + 4440: '44', + 4450: '45', + 5510: '51', + 5520: '52', + 5530: '53', + 5550: '55', + 5580: '58', + 6600: '60', + 6610: '61', + 6620: '62', + 6630: '63', + 8850: '85', + 8860: '86', + 8870: '87', + 8880: '88', + 8890: '89', + }, + 'saint-sulpice-la-pointe': { + 'zenbus:Line:659570001:LOC': '1', + 'zenbus:Line:673260001:LOC': '2', + }, + 'buss-saintes': { + 'zenbus:Line:4636451594567680:LOC': 'Charriers', + 'zenbus:Line:463970003:LOC': 'C', + 'zenbus:Line:4678499588636672:LOC': 'L7', + 'zenbus:Line:4684295948992512:LOC': 'L13', + 'zenbus:Line:469580007:LOC': 'A', + 'zenbus:Line:4700635313209344:LOC': 'L9', + 'zenbus:Line:4729172451655680:LOC': 'RPI 41', + 'zenbus:Line:4758574388477952:LOC': 'L3', + 'zenbus:Line:4769414919487488:LOC': 'L16', + 'zenbus:Line:4789313435860992:LOC': 'SCO_6', + 'zenbus:Line:4793812195999744:LOC': 'L18HC', + 'zenbus:Line:4795297315160064:LOC': 'SCO_7', + 'zenbus:Line:482840002:LOC': 'B', + 'zenbus:Line:4837514293542912:LOC': 'SCO_1', + 'zenbus:Line:4844465832329216:LOC': 'SCO_3', + 'zenbus:Line:4845535883165696:LOC': 'TAD', + 'zenbus:Line:4846561189167104:LOC': 'L16HC', + 'zenbus:Line:4858923900207104:LOC': 'A_B_C', + 'zenbus:Line:4860970670227456:LOC': 'L07HC', + 'zenbus:Line:4873911540908032:LOC': 'L13HC', + 'zenbus:Line:4911775972589568:LOC': 'SCO_4', + 'zenbus:Line:4917472961495040:LOC': 'L05HC', + 'zenbus:Line:4922491244904448:LOC': 'SCO_2', + 'zenbus:Line:4946613626732544:LOC': 'C+', + 'zenbus:Line:4990610835505152:LOC': 'L20', + 'zenbus:Line:5005878907371520:LOC': 'L14', + 'zenbus:Line:5018425479921664:LOC': 'L2', + 'zenbus:Line:5112872012087296:LOC': 'L18', + 'zenbus:Line:5243912470921216:LOC': 'RPI 32', + 'zenbus:Line:5301388289507328:LOC': 'L21', + 'zenbus:Line:5316368099442688:LOC': 'L19', + 'zenbus:Line:5334917157224448:LOC': 'RPI 38', + 'zenbus:Line:5340697109463040:LOC': 'RPI 33', + 'zenbus:Line:5479864732745728:LOC': 'L06_TAD', + 'zenbus:Line:5517036387041280:LOC': 'L6', + 'zenbus:Line:5655741818994688:LOC': 'RPI 35', + 'zenbus:Line:5768778748002304:LOC': 'L17', + 'zenbus:Line:5847778665168896:LOC': 'RPI 34', + 'zenbus:Line:5885429636136960:LOC': 'L03_TAD', + 'zenbus:Line:5888437140848640:LOC': 'RPI 31', + 'zenbus:Line:5889330762481664:LOC': 'L15', + 'zenbus:Line:5919712102842368:LOC': 'SCO_5', + 'zenbus:Line:6013924609622016:LOC': 'RPI 36', + 'zenbus:Line:6020045843988480:LOC': 'L09HC', + 'zenbus:Line:6061716589772800:LOC': 'NAV_Q', + 'zenbus:Line:6116510742347776:LOC': 'L5', + 'zenbus:Line:6356451237822464:LOC': 'L8', + 'zenbus:Line:6387340944605184:LOC': 'L11', + 'zenbus:Line:6453133099139072:LOC': 'L4', + 'zenbus:Line:6480967972683776:LOC': 'RPI_3035', + 'zenbus:Line:6511208539619328:LOC': 'RPI 37', + 'zenbus:Line:6555075053879296:LOC': 'L1', + 'zenbus:Line:6583809626603520:LOC': 'L12', + 'zenbus:Line:6600113322459136:LOC': 'NAV_A', + 'zenbus:Line:6616817792450560:LOC': 'RPI 39', + }, + 'sarlat-bus': { + 'zenbus:Line:723780009:LOC': 'A', + 'zenbus:Line:725660008:LOC': 'B', + }, + 'illygo-sarthe': { + 940: '206GO', + 941: '25GO', + 942: '2GO', + 943: '1GO', + 944: '208GO', + 945: '22GO', + 946: '211GO', + 948: '22GO ETE', + 949: '3GO', + 958: '23GO', + }, + 'transvilles-valenciennes': { + 'NAVINEO:Line::100:LOC': '100', + 'NAVINEO:Line::101:LOC': '101', + 'NAVINEO:Line::102:LOC': '102', + 'NAVINEO:Line::103:LOC': '103', + 'NAVINEO:Line::106:LOC': '106', + 'NAVINEO:Line::107:LOC': '107', + 'NAVINEO:Line::108:LOC': '108', + 'NAVINEO:Line::109:LOC': '109', + 'NAVINEO:Line::10:LOC': '10', + 'NAVINEO:Line::112:LOC': '112', + 'NAVINEO:Line::113:LOC': '113', + 'NAVINEO:Line::115:LOC': '115', + 'NAVINEO:Line::116:LOC': '116', + 'NAVINEO:Line::11:LOC': '11', + 'NAVINEO:Line::12:LOC': '12', + 'NAVINEO:Line::131:LOC': '131', + 'NAVINEO:Line::133:LOC': '133', + 'NAVINEO:Line::134:LOC': '134', + 'NAVINEO:Line::13:LOC': '13', + 'NAVINEO:Line::140:LOC': 'IGO1', + 'NAVINEO:Line::14:LOC': '14', + 'NAVINEO:Line::15:LOC': '15', + 'NAVINEO:Line::16:LOC': '16', + 'NAVINEO:Line::178:LOC': 'IGO2', + 'NAVINEO:Line::17:LOC': '17', + 'NAVINEO:Line::186:LOC': 'H', + 'NAVINEO:Line::187:LOC': 'C2', + 'NAVINEO:Line::188:LOC': 'C3', + 'NAVINEO:Line::18:LOC': '18', + 'NAVINEO:Line::190:LOC': 'T1', + 'NAVINEO:Line::191:LOC': 'T2', + 'NAVINEO:Line::194:LOC': 'AMD', + 'NAVINEO:Line::197:LOC': 'C1', + 'NAVINEO:Line::198:LOC': 'VLX', + 'NAVINEO:Line::19:LOC': '19', + 'NAVINEO:Line::1:LOC': 'L1', + 'NAVINEO:Line::200:LOC': 'SCO', + 'NAVINEO:Line::201:LOC': 'P44', + 'NAVINEO:Line::203:LOC': 'P26', + 'NAVINEO:Line::20:LOC': '20', + 'NAVINEO:Line::212:LOC': 'P80', + 'NAVINEO:Line::213:LOC': 'P21', + 'NAVINEO:Line::214:LOC': 'P70', + 'NAVINEO:Line::215:LOC': 'P20', + 'NAVINEO:Line::216:LOC': 'P18', + 'NAVINEO:Line::218:LOC': 'P15', + 'NAVINEO:Line::219:LOC': 'P48', + 'NAVINEO:Line::225:LOC': 'P27', + 'NAVINEO:Line::227:LOC': 'P11', + 'NAVINEO:Line::228:LOC': 'P14', + 'NAVINEO:Line::230:LOC': 'P37', + 'NAVINEO:Line::231:LOC': 'P16', + 'NAVINEO:Line::233:LOC': 'P19', + 'NAVINEO:Line::235:LOC': 'P04', + 'NAVINEO:Line::236:LOC': 'P28', + 'NAVINEO:Line::238:LOC': 'P17', + 'NAVINEO:Line::241:LOC': 'P90', + 'NAVINEO:Line::242:LOC': 'P07', + 'NAVINEO:Line::243:LOC': 'P60', + 'NAVINEO:Line::244:LOC': 'P02', + 'NAVINEO:Line::26:LOC': 'A', + 'NAVINEO:Line::27:LOC': 'G', + 'NAVINEO:Line::2:LOC': 'L2', + 'NAVINEO:Line::3:LOC': '3', + 'NAVINEO:Line::4:LOC': '4', + 'NAVINEO:Line::5:LOC': '5', + 'NAVINEO:Line::7:LOC': 'CRL', + }, + 'kiceo-vannes': { + 100: 'S339', + 101: 'S342', + 102: 'S343', + 103: 'S344', + 104: 'S344_BIS', + 105: 'S344_D', + 106: 'S345', + 107: 'S345_BIS', + 108: 'S346', + 109: 'S347', + 110: 'S370', + 111: 'S348', + 112: 'S371', + 114: 'S378', + 115: 'S387', + 116: 'S377', + 117: 'S388', + 119: 'S385', + 120: 'S376', + 122: 'S374', + 123: 'S390', + 125: 'S360', + 126: 'S372', + 127: 'S361', + 128: 'S373', + 13: '20', + 130: 'S362', + 131: 'S363', + 132: 'S364', + 133: 'S365', + 134: 'S402', + 135: 'S438', + 136: 'S439', + 137: 'S440', + 138: 'S441', + 139: 'S442', + 14: '21', + 140: 'S443', + 141: 'S444', + 142: 'S445', + 143: 'S446', + 144: 'S447', + 145: 'S448', + 146: 'S449', + 147: 'S480', + 148: 'S481', + 149: 'S482', + 15: '22', + 150: 'S501', + 152: 'S503', + 153: 'S504', + 154: 'S505', + 155: 'S506', + 156: 'S507', + 157: 'S508', + 158: 'S509', + 159: 'S510', + 16: '23', + 160: 'S511', + 161: 'S512', + 162: 'S513', + 163: 'S514', + 164: 'S515', + 165: 'S610', + 166: 'S614_L25', + 167: 'S612_L25', + 168: 'S620', + 169: 'S621', + 17: '24', + 170: 'S640', + 171: 'S650', + 173: 'S670', + 174: 'S671', + 175: 'S672', + 176: 'S680', + 177: 'S681', + 178: 'S82', + 179: 'S96', + 18: '25', + 181: 'S98', + 182: 'SCOO_302', + 183: 'SGCP_301', + 184: 'SGCP_303', + 185: 'SGCP_304', + 186: 'SPLN_301', + 187: 'SR_347', + 188: 'SRL1', + 189: 'SRL4', + 19: '6a', + 191: 'SRL6A', + 192: 'SRL6B', + 193: 'SRL7', + 194: 'SXLL_303', + 20: '6b', + 21: 'TRM', + 22: 'D1', + 23: 'D2', + 24: 'D3', + 26: 'S103_L25', + 27: 'S104_L25', + 28: 'S105_L25', + 29: 'S106_L25', + 30: 'S107_L25', + 32: 'S108_L25', + 33: 'S109_L25', + 338: '30', + 34: 'S110_L25', + 344: 'D4', + 346: 'DS - A', + 348: '27', + 35: 'S111_L25', + 36: 'S112_L25', + 366: '9', + 367: 'S119', + 368: 'S301', + 369: '603', + 37: 'S113_L25', + 370: '604', + 372: 'S375', + 373: '609', + 374: '611', + 375: '606', + 376: '608', + 377: '602', + 379: 'S386', + 38: 'S120', + 381: 'DS - B', + 382: 'S115_L25', + 383: 'SXLL_304', + 384: 'S114_L25', + 386: 'S97', + 39: 'S204_L25', + 40: 'S205_L25', + 41: 'S206_L25', + 42: 'S207_L25', + 43: 'S208_L25', + 44: 'S209_L25', + 45: 'S211', + 46: 'S213', + 47: 'S213_BIS', + 48: 'S213_TER', + 49: 'S215', + 50: 'S260', + 51: 'S261', + 52: 'S261_BIS', + 53: 'S261_TER', + 54: 'S263', + 55: 'S263_BIS', + 56: 'S264', + 57: 'S281', + 58: 'S282', + 59: 'S283', + 6: 'S101_L25', + 60: 'S284', + 61: 'S285', + 62: 'S287', + 63: 'S288', + 64: 'S302', + 65: 'S302_NAV', + 66: 'S303', + 67: 'S303_SURZUR', + 68: 'S389', + 69: 'S315', + 70: 'S315_COLLEGE', + 72: 'S316', + 73: 'S317', + 74: 'S317_COLLEGE', + 75: 'S318', + 76: 'S319', + 77: 'S319_COLLEGE', + 78: 'S320', + 79: 'S321', + 80: 'S321_COLLEGE', + 81: 'S322', + 82: 'S322_COLLEGE', + 83: 'S323', + 84: 'S324', + 85: 'S325', + 86: 'S326', + 87: 'S326_BIS', + 89: 'S327', + 9: 'S102_L25', + 91: 'S328', + 92: 'S329', + 93: 'S334', + 94: 'S335', + 95: 'S335_', + 96: 'S336', + 97: 'S337', + }, + 'move-vendome': { + 'LIGNE A': 'A', + 'LIGNE B': 'B', + 'LIGNE C': 'C', + 'LIGNE D': 'D', + 'LIGNE E': 'E', + 'LIGNE ESAT': 'ESAT', + 'LIGNE F': 'F', + 'LIGNE G': 'G', + 'LIGNE H': 'H', + 'LIGNE M': 'M', + 'NAVETTE TGV': 'TGV', + }, + 'sngo-vernon': { + 'ATOUMOD007:Line:10:LOC': '10', + 'ATOUMOD007:Line:21:LOC': '1', + 'ATOUMOD007:Line:22:LOC': '2', + 'ATOUMOD007:Line:23:LOC': '3', + 'ATOUMOD007:Line:24:LOC': '4', + 'ATOUMOD007:Line:25:LOC': '5', + 'ATOUMOD007:Line:26:LOC': '6', + 'ATOUMOD007:Line:27:LOC': '7', + 'ATOUMOD007:Line:28:LOC': '8', + 'ATOUMOD007:Line:29:LOC': '9', + 'ATOUMOD007:Line:31:LOC': '11', + 'ATOUMOD007:Line:92:LOC': 'DJF', + }, + 'vib-vierzon': { + 1: 'A', + 10: 'Vierzon - Romorantin', + 11: 'Ligne de Marché', + 2: 'B', + 3: 'C', + 4: 'D', + 5: 'E', + 9: 'Bourges - Vierzon', + }, + 'vikibus-yvetot': { + 'ATOUMOD012:Line:1:LOC': '1', + 'ATOUMOD012:Line:2:LOC': '2', + 'ATOUMOD012:Line:3:LOC': '3', + 'ATOUMOD012:Line:Navette:LOC': 'Navette', + }, + 'transurbain-evreux': { + 'ATOUMOD004:Line:T10:LOC': 'T10', + 'ATOUMOD004:Line:T1:LOC': 'T1', + 'ATOUMOD004:Line:T2:LOC': 'T2', + 'ATOUMOD004:Line:T3:LOC': 'T3', + 'ATOUMOD004:Line:T4:LOC': 'T4', + 'ATOUMOD004:Line:T5:LOC': 'T5', + 'ATOUMOD004:Line:T6:LOC': 'T6', + 'ATOUMOD004:Line:T7:LOC': 'T7', + 'ATOUMOD004:Line:T8:LOC': 'T8', + 'ATOUMOD004:Line:T9:LOC': 'T9', + }, +};