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.
This commit is contained in:
Said Turksever committed 2026-11-13 15:38:18 +00:00
1 parent 42540ad85e
commit b3ffe9beb2
8 files changed
+3822 -1

No files matched your search

+57
View File
@@ -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/<id>` 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<object>}
*/
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),
]);
/**