From b8717736bba97052db2bbe29631fd3a328a06f1e Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 18 Sep 2026 14:44:49 +0300 Subject: [PATCH] Add support for GTFS taxiProvider feeds and add explicit transitFeeds configuration. --- README.md | 28 +++++++++---------- configs/finland/config.js | 15 ++++++---- configs/finland/otp-config.json | 2 +- configs/varely/config.js | 15 ++++++---- configs/varely/otp-config.json | 2 +- configs/waltti-alt/config.js | 15 ++++++---- configs/waltti-alt/otp-config.json | 2 +- configs/waltti/config.js | 15 ++++++---- configs/waltti/otp-config.json | 2 +- gulpfile.js | 44 ------------------------------ src/tasks/BuildOTPGraph.js | 1 - src/tasks/PrepareRouterData.js | 29 ++++++++++++++------ src/tasks/Update.js | 1 - src/utils/configUtils.js | 5 +++- 14 files changed, 77 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 798d6bd7..d966605f 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ timezone is logged once at startup. - `netex:update` downloads NeTEx packages configured in a router's `netex` list and copies them, renamed to `-netex.zip`, to the `data/ready/netex` directory. Unlike `gtfs:update`, this data is not fitted, filtered, or otherwise processed — it is only downloaded and renamed. -- `taxiZone:update` downloads GTFS packages configured in a router's `taxiZone` list (separate from the regular `gtfs` list, used for OpenTripPlanner's taxi zone feature) and copies them, renamed to `-taxizone.zip`, to the `data/ready/taxizone` directory. Works the same way as `netex:update` above. +- `router:copy`/`router:copyForPrebuiltStreetGraphDataBuild` (via `PrepareRouterData.js`) write the router's `build-config.json`. Every configured `gtfs` and `netex` feed is listed explicitly in its `transitFeeds` array (with `feedId`/`source`, and `taxiProvider: true` for taxi provider feeds). This is required because OTP disables local directory auto-scanning for an entire feed type (`gtfs` or `netex`) as soon as any feed of that type is declared in `transitFeeds` — a partial/mixed list would cause the undeclared feeds of that type to be silently dropped. - `router:buildGraph` @@ -152,15 +152,14 @@ timezone is logged once at startup. - `gtfs:filter` - `gtfs:id` 5. `netex:update` -6. `taxiZone:update` -7. `router:buildGraph` +6. `router:buildGraph` - `router:copy` - `buildOTPGraphTask(config.router)` -8. `test.sh` -9. `router:store` -10. `deploy.sh` -11. `deploy-otp.sh` -12. `storage:cleanup` +7. `test.sh` +8. `router:store` +9. `deploy.sh` +10. `deploy-otp.sh` +11. `storage:cleanup` #### Street only build @@ -182,15 +181,14 @@ timezone is logged once at startup. - `gtfs:filter` - `gtfs:id` 3. `netex:update` -4. `taxiZone:update` -5. `router:buildWithPrebuiltStreetGraph` +4. `router:buildWithPrebuiltStreetGraph` - `router:copyForPrebuiltStreetGraphDataBuild` - `buildOTPGraphTask(config.router)` -6. `test.sh` -7. `router:storeForPrebuiltStreetGraphDataBuild` -8. `deploy.sh` -9. `deploy-otp.sh` -10. `storage:cleanup` +5. `test.sh` +6. `router:storeForPrebuiltStreetGraphDataBuild` +7. `deploy.sh` +8. `deploy-otp.sh` +9. `storage:cleanup` ### otp-data-tools diff --git a/configs/finland/config.js b/configs/finland/config.js index 35fe72da..7f9d25d9 100644 --- a/configs/finland/config.js +++ b/configs/finland/config.js @@ -116,12 +116,15 @@ module.exports = { 'https://minfoapi.matkahuolto.fi/gtfs/177/gtfs.zip', true, ), - ], - taxiZone: [ - { - id: '02Taksi', - url: 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', - }, + mapSrc( + '02Taksi', + 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', + undefined, + undefined, + undefined, + undefined, + true, + ), ], netex: [ { diff --git a/configs/finland/otp-config.json b/configs/finland/otp-config.json index 5ece5f32..f4714881 100644 --- a/configs/finland/otp-config.json +++ b/configs/finland/otp-config.json @@ -12,6 +12,6 @@ "Emission": true, "DebugRasterTiles": true, "FlexRouting": true, - "TaxiZone": true + "TaxiRouting": true } } diff --git a/configs/varely/config.js b/configs/varely/config.js index 2a4f3095..5838a3ed 100644 --- a/configs/varely/config.js +++ b/configs/varely/config.js @@ -14,12 +14,15 @@ module.exports = { ), mapSrc('Salo', 'https://tvv.fra1.digitaloceanspaces.com/239.zip', true), mapSrc('Pori', 'https://tvv.fra1.digitaloceanspaces.com/231.zip', true), - ], - taxiZone: [ - { - id: '02Taksi', - url: 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', - }, + mapSrc( + '02Taksi', + 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', + undefined, + undefined, + undefined, + undefined, + true, + ), ], osm: ['varely'], }; diff --git a/configs/varely/otp-config.json b/configs/varely/otp-config.json index 434c7bcc..ee2dbfd6 100644 --- a/configs/varely/otp-config.json +++ b/configs/varely/otp-config.json @@ -9,6 +9,6 @@ "ActuatorAPI": true, "AsyncGraphQLFetchers": false, "DebugRasterTiles": true, - "TaxiZone": true + "TaxiRouting": true } } diff --git a/configs/waltti-alt/config.js b/configs/waltti-alt/config.js index 62763bdd..1325b64c 100644 --- a/configs/waltti-alt/config.js +++ b/configs/waltti-alt/config.js @@ -32,12 +32,15 @@ module.exports = { 'https://github.com/tvv-lippu-ja-maksujarjestelma-oy/waltti-digitransit-config-data/raw/refs/heads/main/GTFStestireittiopas.zip', 'true', ), - ], - taxiZone: [ - { - id: '02Taksi', - url: 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', - }, + mapSrc( + '02Taksi', + 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', + undefined, + undefined, + undefined, + undefined, + true, + ), ], osm: ['oulu', 'southFinland'], }; diff --git a/configs/waltti-alt/otp-config.json b/configs/waltti-alt/otp-config.json index 434c7bcc..ee2dbfd6 100644 --- a/configs/waltti-alt/otp-config.json +++ b/configs/waltti-alt/otp-config.json @@ -9,6 +9,6 @@ "ActuatorAPI": true, "AsyncGraphQLFetchers": false, "DebugRasterTiles": true, - "TaxiZone": true + "TaxiRouting": true } } diff --git a/configs/waltti/config.js b/configs/waltti/config.js index bfa1476e..4c368f6a 100644 --- a/configs/waltti/config.js +++ b/configs/waltti/config.js @@ -127,12 +127,15 @@ module.exports = { ), mapSrc('Salo', 'https://tvv.fra1.digitaloceanspaces.com/239.zip', true), mapSrc('Kajaani', 'https://tvv.fra1.digitaloceanspaces.com/211.zip', true), - ], - taxiZone: [ - { - id: '02Taksi', - url: 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', - }, + mapSrc( + '02Taksi', + 'https://gtfsdata.blob.core.windows.net/finland/02Taksi-taxizone.zip', + undefined, + undefined, + undefined, + undefined, + true, + ), ], osm: ['kajaani', 'oulu', 'rovaniemi', 'southFinland', 'vaasa'], }; diff --git a/configs/waltti/otp-config.json b/configs/waltti/otp-config.json index 8849a2f3..2e39ee5f 100644 --- a/configs/waltti/otp-config.json +++ b/configs/waltti/otp-config.json @@ -11,6 +11,6 @@ "AsyncGraphQLFetchers": false, "Emission": true, "DebugRasterTiles": true, - "TaxiZone": true + "TaxiRouting": true } } diff --git a/gulpfile.js b/gulpfile.js index eab043e0..5be04e95 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -68,13 +68,11 @@ const osmDlDir = `${config.dataDir}/downloads/osm`; const demDlDir = `${config.dataDir}/downloads/dem`; const gtfsDlDir = `${config.dataDir}/downloads/gtfs`; const netexDlDir = `${config.dataDir}/downloads/netex`; -const taxiZoneDlDir = `${config.dataDir}/downloads/taxizone`; const osmDir = `${config.dataDir}/ready/osm`; const demDir = `${config.dataDir}/ready/dem`; const gtfsDir = `${config.dataDir}/ready/gtfs`; const netexDir = `${config.dataDir}/ready/netex`; -const taxiZoneDir = `${config.dataDir}/ready/taxizone`; const gtfsSeedDir = `${config.dataDir}/seed`; const fitDir = `${config.dataDir}/fit`; @@ -108,29 +106,6 @@ gulp.task('netex:rename', () => gulp.task('netex:update', gulp.series('netex:download', 'netex:rename')); -/** - * Download taxi zone data - */ -gulp.task('taxiZone:download', () => { - if (!config.router.taxiZone) { - return Promise.resolve(); - } - return dl(config.router.taxiZone, taxiZoneDlDir); -}); - -gulp.task('taxiZone:rename', () => - pipeline( - gulp.src(`${taxiZoneDlDir}/*`, noBuf), - renameFile('-taxizone'), - gulp.dest(taxiZoneDir), - ), -); - -gulp.task( - 'taxiZone:update', - gulp.series('taxiZone:download', 'taxiZone:rename'), -); - /** * Download osm data */ @@ -366,24 +341,6 @@ gulp.task( ), ); -gulp.task('taxiZone:del', () => { - logger.info('Deleting old taxi zone data...'); - return del(taxiZoneDir); -}); - -gulp.task( - 'taxiZone:seed', - gulp.series( - 'taxiZone:del', - named('taxiZone:seed:copyFiles', () => - pipeline( - gulp.src(`${seedSourceDir}/*-taxizone.zip`, noBuf), - gulp.dest(taxiZoneDir), - ), - ), - ), -); - gulp.task('osm:del', () => { logger.info('Deleting old OSM data...'); return del(osmDir); @@ -438,7 +395,6 @@ gulp.task( 'osm:seed', 'gtfs:seed', 'netex:seed', - 'taxiZone:seed', 'seed:cleanup', ), ); diff --git a/src/tasks/BuildOTPGraph.js b/src/tasks/BuildOTPGraph.js index 5b37f66f..872ce0d7 100644 --- a/src/tasks/BuildOTPGraph.js +++ b/src/tasks/BuildOTPGraph.js @@ -92,7 +92,6 @@ const packData = function (commit, router) { `${path}/router-${router.id}.zip`, [ `${path}/*-gtfs.zip`, - `${path}/*-taxizone.zip`, `${path}/*-netex.zip`, `${path}/*.json`, ...osmFiles, diff --git a/src/tasks/PrepareRouterData.js b/src/tasks/PrepareRouterData.js index 6c55c5b9..b0076ced 100644 --- a/src/tasks/PrepareRouterData.js +++ b/src/tasks/PrepareRouterData.js @@ -23,6 +23,12 @@ const extraUpdaters = ? JSON.parse(process.env.EXTRA_UPDATERS) : {}; +/** + * Builds the `transitFeeds` array for build-config.json. Every configured gtfs and netex + * feed must be listed explicitly here: OTP disables local directory auto-scanning for an + * entire feed type (gtfs or netex) as soon as one feed of that type is declared, so a + * partial list would silently drop any undeclared feeds of that type. + */ function createAndProcessBuildConfig(router) { logger.info('Creating build-config.json...'); const configName = `${configsDir}/${router.id}/build-config.json`; @@ -39,17 +45,22 @@ function createAndProcessBuildConfig(router) { }; transitFeeds.push(feed); }); - buildConfig.transitFeeds = transitFeeds; } - if (router.taxiZone) { - const taxiZoneFeeds = buildConfig.taxiZone?.feeds || []; - router.taxiZone.forEach(src => { - taxiZoneFeeds.push({ + if (router.gtfs) { + router.gtfs.forEach(src => { + const feed = { + type: 'gtfs', feedId: src.id, - source: 'file:///var/opentripplanner/' + src.id + '-taxizone.zip', - }); + source: 'file:///var/opentripplanner/' + src.id + '-gtfs.zip', + }; + if (src.taxiProvider) { + feed.taxiProvider = true; + } + transitFeeds.push(feed); }); - buildConfig.taxiZone = { ...buildConfig.taxiZone, feeds: taxiZoneFeeds }; + } + if (transitFeeds.length > 0) { + buildConfig.transitFeeds = transitFeeds; } const file = new Vinyl({ path: 'build-config.json', @@ -95,7 +106,7 @@ function createAndProcessRouterConfig(router) { function getTransitDataFiles(router) { const files = []; - ['gtfs', 'netex', 'taxiZone'].forEach(type => { + ['gtfs', 'netex'].forEach(type => { if (router[type]) { const dirName = type.toLowerCase(); router[type].forEach(src => { diff --git a/src/tasks/Update.js b/src/tasks/Update.js index 619715b7..dfc7ecd1 100644 --- a/src/tasks/Update.js +++ b/src/tasks/Update.js @@ -78,7 +78,6 @@ async function handleOsmAndDemUpdate() { async function handleTransitDataUpdate() { await start('gtfs:update'); await start('netex:update'); - await start('taxiZone:update'); } function handleTests() { diff --git a/src/utils/configUtils.js b/src/utils/configUtils.js index 8208549e..f039bd7f 100644 --- a/src/utils/configUtils.js +++ b/src/utils/configUtils.js @@ -10,14 +10,17 @@ * @param {Object.} [replacements] - Map of file to replace -> replacement * file name (or `null` to just remove the file), applied before packaging. * @param {Object} [request] - Extra axios request options (e.g. custom headers) for the download. + * @param {boolean} [taxiProvider] - Whether this feed is exclusively a source of taxi + * provider data (OTP's `taxiProvider` build-config flag). */ -const mapSrc = (id, url, fit, rules, replacements, request) => ({ +const mapSrc = (id, url, fit, rules, replacements, request, taxiProvider) => ({ id, url, fit, rules, replacements, request, + taxiProvider, }); /**