hk-bus-eta 3.8.2 → 3.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/journeyTime.d.ts +4 -3
- package/dist/journeyTime.js +45 -54
- package/dist/journeyTime.js.map +1 -1
- package/esm/journeyTime.d.ts +4 -3
- package/esm/journeyTime.js +47 -52
- package/esm/journeyTime.js.map +1 -1
- package/package.json +1 -2
- package/src/journeyTime.ts +64 -86
package/dist/journeyTime.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { RouteListEntry, StopList } from "./type";
|
|
1
|
+
import { EtaDb, RouteListEntry, StopList } from "./type";
|
|
2
2
|
/**
|
|
3
3
|
* Fetch Journey time in minute for a route
|
|
4
4
|
* @param {Object}
|
|
5
|
-
* @returns {number} journey time in minute
|
|
5
|
+
* @returns {number} journey time in minute, not rounded
|
|
6
6
|
*/
|
|
7
|
-
export declare function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize, signal, }: {
|
|
7
|
+
export declare function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize, holidays, signal, }: {
|
|
8
8
|
route: RouteListEntry;
|
|
9
9
|
stopList: StopList;
|
|
10
10
|
startSeq: number;
|
|
11
11
|
endSeq: number;
|
|
12
12
|
batchSize?: number;
|
|
13
|
+
holidays?: EtaDb["holidays"];
|
|
13
14
|
signal?: AbortSignal | null;
|
|
14
15
|
}): Promise<number>;
|
package/dist/journeyTime.js
CHANGED
|
@@ -12,52 +12,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.fetchEstJourneyTime = void 0;
|
|
13
13
|
const date_fns_tz_1 = require("date-fns-tz");
|
|
14
14
|
const __HK_BUS_ETA_JT_CACHE__ = {};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return Promise.all(requests).then((seconds) => Math.ceil(seconds.reduce((acc, cur) => acc + cur, 0) / 60));
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
function fetchEstJourneyTimeBasedOnHistoricalAvgData({ route, startSeq, endSeq, }) {
|
|
37
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
const requests = [];
|
|
39
|
-
const stops = Object.values(route.stops)[0];
|
|
40
|
-
for (let i = startSeq; i < endSeq; ++i) {
|
|
41
|
-
const start = stops[i];
|
|
42
|
-
const end = stops[i + 1];
|
|
43
|
-
requests.push(fetch(`https://raw.githubusercontent.com/HK-Bus-ETA/hk-bus-time-between-stops/refs/heads/pages/times/${start.slice(0, 2)}.json`)
|
|
44
|
-
.then((r) => r.json())
|
|
45
|
-
.then((r) => {
|
|
46
|
-
if (r[start][end]) {
|
|
47
|
-
return r[start][end];
|
|
48
|
-
}
|
|
49
|
-
throw new Error("not found");
|
|
50
|
-
}));
|
|
15
|
+
const TIME_BETWEEN_STOPS_BASE_URL = "https://raw.githubusercontent.com/HK-Bus-ETA/hk-bus-time-between-stops/refs/heads/pages";
|
|
16
|
+
// hk-bus-time-between-stops names weekday folders by Python's %w (Sunday = 0)
|
|
17
|
+
// and files public holidays under 0 as well
|
|
18
|
+
const getTimesHourlyPath = (holidays, now) => {
|
|
19
|
+
const day = holidays.includes((0, date_fns_tz_1.formatInTimeZone)(now, "Asia/Hong_Kong", "yyyyMMdd"))
|
|
20
|
+
? 0
|
|
21
|
+
: parseInt((0, date_fns_tz_1.formatInTimeZone)(now, "Asia/Hong_Kong", "i"), 10) % 7;
|
|
22
|
+
const hour = (0, date_fns_tz_1.formatInTimeZone)(now, "Asia/Hong_Kong", "HH");
|
|
23
|
+
return `times_hourly/${day}/${hour}`;
|
|
24
|
+
};
|
|
25
|
+
function fetchSecondsBetweenStops({ path, start, end, signal, }) {
|
|
26
|
+
return fetch(`${TIME_BETWEEN_STOPS_BASE_URL}/${path}/${start.slice(0, 2)}.json`, { signal })
|
|
27
|
+
.then((r) => r.json())
|
|
28
|
+
.then((r) => {
|
|
29
|
+
var _a;
|
|
30
|
+
const seconds = (_a = r[start]) === null || _a === void 0 ? void 0 : _a[end];
|
|
31
|
+
if (seconds) {
|
|
32
|
+
return seconds;
|
|
51
33
|
}
|
|
52
|
-
|
|
34
|
+
throw new Error("not found");
|
|
53
35
|
});
|
|
54
36
|
}
|
|
55
37
|
/**
|
|
56
38
|
* Fetch Journey time in minute for a route
|
|
57
39
|
* @param {Object}
|
|
58
|
-
* @returns {number} journey time in minute
|
|
40
|
+
* @returns {number} journey time in minute, not rounded
|
|
59
41
|
*/
|
|
60
|
-
function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4, signal, }) {
|
|
42
|
+
function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4, holidays = [], signal, }) {
|
|
61
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
62
44
|
const stops = Object.values(route.stops)[0];
|
|
63
45
|
if (!["kmb", "nlb", "ctb", "lrtfeeder", "gmb"].includes(Object.keys(route.stops)[0]))
|
|
@@ -70,6 +52,7 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
|
|
|
70
52
|
throw new Error("startSeq should be < endSeq");
|
|
71
53
|
let ts = Date.now();
|
|
72
54
|
let ret = 0;
|
|
55
|
+
const timesHourlyPath = getTimesHourlyPath(holidays, new Date(ts));
|
|
73
56
|
let payloads = [];
|
|
74
57
|
for (let i = startSeq; i < endSeq; ++i) {
|
|
75
58
|
payloads.push([
|
|
@@ -86,30 +69,35 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
|
|
|
86
69
|
departIn: Math.round(ret / 15) * 15,
|
|
87
70
|
}),
|
|
88
71
|
{
|
|
89
|
-
|
|
90
|
-
|
|
72
|
+
start: stops[i],
|
|
73
|
+
end: stops[i + 1],
|
|
91
74
|
},
|
|
92
75
|
]);
|
|
93
76
|
if (payloads.length < batchSize && i !== endSeq - 1) {
|
|
94
77
|
// skip fetching until whole batch filled
|
|
95
78
|
continue;
|
|
96
79
|
}
|
|
97
|
-
const
|
|
80
|
+
const minutes = yield Promise.all(payloads.map(([key, payload, { start, end }]) => {
|
|
98
81
|
// load from cache if it is query within 15 minutes
|
|
99
82
|
if (key in __HK_BUS_ETA_JT_CACHE__ &&
|
|
100
83
|
__HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
|
|
101
84
|
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].s);
|
|
102
85
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
86
|
+
// sum in seconds and convert once; rounding each stop-to-stop segment
|
|
87
|
+
// up to a whole minute overestimates by ~0.5 minute per stop
|
|
88
|
+
return fetchSecondsBetweenStops({
|
|
89
|
+
path: timesHourlyPath,
|
|
90
|
+
start,
|
|
91
|
+
end,
|
|
92
|
+
signal,
|
|
107
93
|
})
|
|
108
|
-
.catch(() =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
94
|
+
.catch(() => fetchSecondsBetweenStops({
|
|
95
|
+
path: "times",
|
|
96
|
+
start,
|
|
97
|
+
end,
|
|
98
|
+
signal,
|
|
112
99
|
}))
|
|
100
|
+
.then((seconds) => seconds / 60)
|
|
113
101
|
.catch(() => fetch("https://tdas-api.hkemobility.gov.hk/tdas/api/route", {
|
|
114
102
|
method: "POST",
|
|
115
103
|
headers: {
|
|
@@ -134,8 +122,11 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
|
|
|
134
122
|
.map((v) => parseInt(v, 10));
|
|
135
123
|
return hh * 60 + mm;
|
|
136
124
|
})
|
|
137
|
-
.catch(() => {
|
|
138
|
-
|
|
125
|
+
.catch((e) => {
|
|
126
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
129
|
+
// for any other error, assume 4 minutes journey time blindly
|
|
139
130
|
return 4;
|
|
140
131
|
})
|
|
141
132
|
.then((s) => {
|
|
@@ -144,8 +135,8 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
|
|
|
144
135
|
return s * 1.1;
|
|
145
136
|
}));
|
|
146
137
|
}));
|
|
147
|
-
|
|
148
|
-
ret +=
|
|
138
|
+
minutes.forEach((m) => {
|
|
139
|
+
ret += m;
|
|
149
140
|
});
|
|
150
141
|
payloads.length = 0;
|
|
151
142
|
}
|
package/dist/journeyTime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,6CAA+C;AAU/C,MAAM,uBAAuB,GAAa,EAAE,CAAC;AAE7C,
|
|
1
|
+
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
|
package/esm/journeyTime.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { RouteListEntry, StopList } from "./type";
|
|
1
|
+
import { EtaDb, RouteListEntry, StopList } from "./type";
|
|
2
2
|
/**
|
|
3
3
|
* Fetch Journey time in minute for a route
|
|
4
4
|
* @param {Object}
|
|
5
|
-
* @returns {number} journey time in minute
|
|
5
|
+
* @returns {number} journey time in minute, not rounded
|
|
6
6
|
*/
|
|
7
|
-
export declare function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize, signal, }: {
|
|
7
|
+
export declare function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize, holidays, signal, }: {
|
|
8
8
|
route: RouteListEntry;
|
|
9
9
|
stopList: StopList;
|
|
10
10
|
startSeq: number;
|
|
11
11
|
endSeq: number;
|
|
12
12
|
batchSize?: number;
|
|
13
|
+
holidays?: EtaDb["holidays"];
|
|
13
14
|
signal?: AbortSignal | null;
|
|
14
15
|
}): Promise<number>;
|
package/esm/journeyTime.js
CHANGED
|
@@ -1,47 +1,33 @@
|
|
|
1
1
|
import { formatInTimeZone } from "date-fns-tz";
|
|
2
2
|
const __HK_BUS_ETA_JT_CACHE__ = {};
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const stops = Object.values(route.stops)[0];
|
|
25
|
-
for (let i = startSeq; i < endSeq; ++i) {
|
|
26
|
-
const start = stops[i];
|
|
27
|
-
const end = stops[i + 1];
|
|
28
|
-
requests.push(fetch(`https://raw.githubusercontent.com/HK-Bus-ETA/hk-bus-time-between-stops/refs/heads/pages/times/${start.slice(0, 2)}.json`)
|
|
29
|
-
.then((r) => r.json())
|
|
30
|
-
.then((r) => {
|
|
31
|
-
if (r[start][end]) {
|
|
32
|
-
return r[start][end];
|
|
33
|
-
}
|
|
34
|
-
throw new Error("not found");
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
return Promise.all(requests).then((seconds) => Math.ceil(seconds.reduce((acc, cur) => acc + cur, 0) / 60));
|
|
3
|
+
const TIME_BETWEEN_STOPS_BASE_URL = "https://raw.githubusercontent.com/HK-Bus-ETA/hk-bus-time-between-stops/refs/heads/pages";
|
|
4
|
+
// hk-bus-time-between-stops names weekday folders by Python's %w (Sunday = 0)
|
|
5
|
+
// and files public holidays under 0 as well
|
|
6
|
+
const getTimesHourlyPath = (holidays, now) => {
|
|
7
|
+
const day = holidays.includes(formatInTimeZone(now, "Asia/Hong_Kong", "yyyyMMdd"))
|
|
8
|
+
? 0
|
|
9
|
+
: parseInt(formatInTimeZone(now, "Asia/Hong_Kong", "i"), 10) % 7;
|
|
10
|
+
const hour = formatInTimeZone(now, "Asia/Hong_Kong", "HH");
|
|
11
|
+
return `times_hourly/${day}/${hour}`;
|
|
12
|
+
};
|
|
13
|
+
function fetchSecondsBetweenStops({ path, start, end, signal, }) {
|
|
14
|
+
return fetch(`${TIME_BETWEEN_STOPS_BASE_URL}/${path}/${start.slice(0, 2)}.json`, { signal })
|
|
15
|
+
.then((r) => r.json())
|
|
16
|
+
.then((r) => {
|
|
17
|
+
var _a;
|
|
18
|
+
const seconds = (_a = r[start]) === null || _a === void 0 ? void 0 : _a[end];
|
|
19
|
+
if (seconds) {
|
|
20
|
+
return seconds;
|
|
21
|
+
}
|
|
22
|
+
throw new Error("not found");
|
|
23
|
+
});
|
|
38
24
|
}
|
|
39
25
|
/**
|
|
40
26
|
* Fetch Journey time in minute for a route
|
|
41
27
|
* @param {Object}
|
|
42
|
-
* @returns {number} journey time in minute
|
|
28
|
+
* @returns {number} journey time in minute, not rounded
|
|
43
29
|
*/
|
|
44
|
-
export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4, signal, }) {
|
|
30
|
+
export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4, holidays = [], signal, }) {
|
|
45
31
|
const stops = Object.values(route.stops)[0];
|
|
46
32
|
if (!["kmb", "nlb", "ctb", "lrtfeeder", "gmb"].includes(Object.keys(route.stops)[0]))
|
|
47
33
|
throw new Error("Support vehicle transport only");
|
|
@@ -53,6 +39,7 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
|
|
|
53
39
|
throw new Error("startSeq should be < endSeq");
|
|
54
40
|
let ts = Date.now();
|
|
55
41
|
let ret = 0;
|
|
42
|
+
const timesHourlyPath = getTimesHourlyPath(holidays, new Date(ts));
|
|
56
43
|
let payloads = [];
|
|
57
44
|
for (let i = startSeq; i < endSeq; ++i) {
|
|
58
45
|
payloads.push([
|
|
@@ -69,30 +56,35 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
|
|
|
69
56
|
departIn: Math.round(ret / 15) * 15,
|
|
70
57
|
}),
|
|
71
58
|
{
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
start: stops[i],
|
|
60
|
+
end: stops[i + 1],
|
|
74
61
|
},
|
|
75
62
|
]);
|
|
76
63
|
if (payloads.length < batchSize && i !== endSeq - 1) {
|
|
77
64
|
// skip fetching until whole batch filled
|
|
78
65
|
continue;
|
|
79
66
|
}
|
|
80
|
-
const
|
|
67
|
+
const minutes = await Promise.all(payloads.map(([key, payload, { start, end }]) => {
|
|
81
68
|
// load from cache if it is query within 15 minutes
|
|
82
69
|
if (key in __HK_BUS_ETA_JT_CACHE__ &&
|
|
83
70
|
__HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
|
|
84
71
|
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].s);
|
|
85
72
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
73
|
+
// sum in seconds and convert once; rounding each stop-to-stop segment
|
|
74
|
+
// up to a whole minute overestimates by ~0.5 minute per stop
|
|
75
|
+
return fetchSecondsBetweenStops({
|
|
76
|
+
path: timesHourlyPath,
|
|
77
|
+
start,
|
|
78
|
+
end,
|
|
79
|
+
signal,
|
|
90
80
|
})
|
|
91
|
-
.catch(() =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
81
|
+
.catch(() => fetchSecondsBetweenStops({
|
|
82
|
+
path: "times",
|
|
83
|
+
start,
|
|
84
|
+
end,
|
|
85
|
+
signal,
|
|
95
86
|
}))
|
|
87
|
+
.then((seconds) => seconds / 60)
|
|
96
88
|
.catch(() => fetch("https://tdas-api.hkemobility.gov.hk/tdas/api/route", {
|
|
97
89
|
method: "POST",
|
|
98
90
|
headers: {
|
|
@@ -117,8 +109,11 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
|
|
|
117
109
|
.map((v) => parseInt(v, 10));
|
|
118
110
|
return hh * 60 + mm;
|
|
119
111
|
})
|
|
120
|
-
.catch(() => {
|
|
121
|
-
|
|
112
|
+
.catch((e) => {
|
|
113
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
114
|
+
throw e;
|
|
115
|
+
}
|
|
116
|
+
// for any other error, assume 4 minutes journey time blindly
|
|
122
117
|
return 4;
|
|
123
118
|
})
|
|
124
119
|
.then((s) => {
|
|
@@ -127,8 +122,8 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
|
|
|
127
122
|
return s * 1.1;
|
|
128
123
|
}));
|
|
129
124
|
}));
|
|
130
|
-
|
|
131
|
-
ret +=
|
|
125
|
+
minutes.forEach((m) => {
|
|
126
|
+
ret += m;
|
|
132
127
|
});
|
|
133
128
|
payloads.length = 0;
|
|
134
129
|
}
|
package/esm/journeyTime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAU/C,MAAM,uBAAuB,GAAa,EAAE,CAAC;AAE7C,
|
|
1
|
+
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hk-bus-eta",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.4",
|
|
4
4
|
"description": "Query the ETA (Estimated Time of Arrival) of HK Bus/Minibus/MTR/Lightrail",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"build:esm": "tsc --target es2018 --outDir esm",
|
|
17
17
|
"build:cjs": "tsc --target es2015 --module commonjs --outDir dist",
|
|
18
18
|
"build": "yarn build:cjs && yarn build:esm",
|
|
19
|
-
"prepublish": "yarn build",
|
|
20
19
|
"est": "ts-node ./est.ts"
|
|
21
20
|
},
|
|
22
21
|
"repository": {
|
package/src/journeyTime.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RouteListEntry, StopList } from "./type";
|
|
1
|
+
import { EtaDb, RouteListEntry, StopList } from "./type";
|
|
2
2
|
import { formatInTimeZone } from "date-fns-tz";
|
|
3
3
|
|
|
4
4
|
type JT_CACHE = Record<
|
|
@@ -11,83 +11,50 @@ type JT_CACHE = Record<
|
|
|
11
11
|
|
|
12
12
|
const __HK_BUS_ETA_JT_CACHE__: JT_CACHE = {};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
startSeq,
|
|
17
|
-
endSeq,
|
|
18
|
-
}: {
|
|
19
|
-
route: RouteListEntry;
|
|
20
|
-
startSeq: number;
|
|
21
|
-
endSeq: number;
|
|
22
|
-
}): Promise<number> {
|
|
23
|
-
const requests = [];
|
|
24
|
-
const stops = Object.values(route.stops)[0];
|
|
14
|
+
const TIME_BETWEEN_STOPS_BASE_URL =
|
|
15
|
+
"https://raw.githubusercontent.com/HK-Bus-ETA/hk-bus-time-between-stops/refs/heads/pages";
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.then((r) => r.json())
|
|
38
|
-
.then((r) => {
|
|
39
|
-
if (r[start][end]) {
|
|
40
|
-
return r[start][end];
|
|
41
|
-
}
|
|
42
|
-
throw new Error("not found");
|
|
43
|
-
}),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return Promise.all(requests).then((seconds) =>
|
|
48
|
-
Math.ceil(seconds.reduce((acc, cur) => acc + cur, 0) / 60),
|
|
49
|
-
);
|
|
50
|
-
}
|
|
17
|
+
// hk-bus-time-between-stops names weekday folders by Python's %w (Sunday = 0)
|
|
18
|
+
// and files public holidays under 0 as well
|
|
19
|
+
const getTimesHourlyPath = (holidays: string[], now: Date): string => {
|
|
20
|
+
const day = holidays.includes(
|
|
21
|
+
formatInTimeZone(now, "Asia/Hong_Kong", "yyyyMMdd"),
|
|
22
|
+
)
|
|
23
|
+
? 0
|
|
24
|
+
: parseInt(formatInTimeZone(now, "Asia/Hong_Kong", "i"), 10) % 7;
|
|
25
|
+
const hour = formatInTimeZone(now, "Asia/Hong_Kong", "HH");
|
|
26
|
+
return `times_hourly/${day}/${hour}`;
|
|
27
|
+
};
|
|
51
28
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
29
|
+
function fetchSecondsBetweenStops({
|
|
30
|
+
path,
|
|
31
|
+
start,
|
|
32
|
+
end,
|
|
33
|
+
signal,
|
|
56
34
|
}: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
35
|
+
path: string;
|
|
36
|
+
start: string;
|
|
37
|
+
end: string;
|
|
38
|
+
signal?: AbortSignal | null;
|
|
60
39
|
}): Promise<number> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
.then((r) => {
|
|
74
|
-
if (r[start][end]) {
|
|
75
|
-
return r[start][end];
|
|
76
|
-
}
|
|
77
|
-
throw new Error("not found");
|
|
78
|
-
}),
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return Promise.all(requests).then((seconds) =>
|
|
83
|
-
Math.ceil(seconds.reduce((acc, cur) => acc + cur, 0) / 60),
|
|
84
|
-
);
|
|
40
|
+
return fetch(
|
|
41
|
+
`${TIME_BETWEEN_STOPS_BASE_URL}/${path}/${start.slice(0, 2)}.json`,
|
|
42
|
+
{ signal },
|
|
43
|
+
)
|
|
44
|
+
.then((r) => r.json())
|
|
45
|
+
.then((r) => {
|
|
46
|
+
const seconds = r[start]?.[end];
|
|
47
|
+
if (seconds) {
|
|
48
|
+
return seconds;
|
|
49
|
+
}
|
|
50
|
+
throw new Error("not found");
|
|
51
|
+
});
|
|
85
52
|
}
|
|
86
53
|
|
|
87
54
|
/**
|
|
88
55
|
* Fetch Journey time in minute for a route
|
|
89
56
|
* @param {Object}
|
|
90
|
-
* @returns {number} journey time in minute
|
|
57
|
+
* @returns {number} journey time in minute, not rounded
|
|
91
58
|
*/
|
|
92
59
|
export async function fetchEstJourneyTime({
|
|
93
60
|
route,
|
|
@@ -95,6 +62,7 @@ export async function fetchEstJourneyTime({
|
|
|
95
62
|
startSeq,
|
|
96
63
|
endSeq,
|
|
97
64
|
batchSize = 4,
|
|
65
|
+
holidays = [],
|
|
98
66
|
signal,
|
|
99
67
|
}: {
|
|
100
68
|
route: RouteListEntry;
|
|
@@ -102,6 +70,7 @@ export async function fetchEstJourneyTime({
|
|
|
102
70
|
startSeq: number;
|
|
103
71
|
endSeq: number;
|
|
104
72
|
batchSize?: number;
|
|
73
|
+
holidays?: EtaDb["holidays"];
|
|
105
74
|
signal?: AbortSignal | null;
|
|
106
75
|
}): Promise<number> {
|
|
107
76
|
const stops = Object.values(route.stops)[0];
|
|
@@ -118,7 +87,8 @@ export async function fetchEstJourneyTime({
|
|
|
118
87
|
|
|
119
88
|
let ts = Date.now();
|
|
120
89
|
let ret = 0;
|
|
121
|
-
|
|
90
|
+
const timesHourlyPath = getTimesHourlyPath(holidays, new Date(ts));
|
|
91
|
+
let payloads: Array<[string, string, Record<string, string>]> = [];
|
|
122
92
|
for (let i = startSeq; i < endSeq; ++i) {
|
|
123
93
|
payloads.push([
|
|
124
94
|
`${stops[i]}-${stops[i + 1]}`,
|
|
@@ -134,16 +104,16 @@ export async function fetchEstJourneyTime({
|
|
|
134
104
|
departIn: Math.round(ret / 15) * 15,
|
|
135
105
|
}),
|
|
136
106
|
{
|
|
137
|
-
|
|
138
|
-
|
|
107
|
+
start: stops[i],
|
|
108
|
+
end: stops[i + 1],
|
|
139
109
|
},
|
|
140
110
|
]);
|
|
141
111
|
if (payloads.length < batchSize && i !== endSeq - 1) {
|
|
142
112
|
// skip fetching until whole batch filled
|
|
143
113
|
continue;
|
|
144
114
|
}
|
|
145
|
-
const
|
|
146
|
-
payloads.map(([key, payload, {
|
|
115
|
+
const minutes = await Promise.all(
|
|
116
|
+
payloads.map(([key, payload, { start, end }]) => {
|
|
147
117
|
// load from cache if it is query within 15 minutes
|
|
148
118
|
if (
|
|
149
119
|
key in __HK_BUS_ETA_JT_CACHE__ &&
|
|
@@ -152,18 +122,23 @@ export async function fetchEstJourneyTime({
|
|
|
152
122
|
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].s);
|
|
153
123
|
}
|
|
154
124
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
125
|
+
// sum in seconds and convert once; rounding each stop-to-stop segment
|
|
126
|
+
// up to a whole minute overestimates by ~0.5 minute per stop
|
|
127
|
+
return fetchSecondsBetweenStops({
|
|
128
|
+
path: timesHourlyPath,
|
|
129
|
+
start,
|
|
130
|
+
end,
|
|
131
|
+
signal,
|
|
159
132
|
})
|
|
160
133
|
.catch(() =>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
134
|
+
fetchSecondsBetweenStops({
|
|
135
|
+
path: "times",
|
|
136
|
+
start,
|
|
137
|
+
end,
|
|
138
|
+
signal,
|
|
165
139
|
}),
|
|
166
140
|
)
|
|
141
|
+
.then((seconds) => seconds / 60)
|
|
167
142
|
.catch(() =>
|
|
168
143
|
fetch("https://tdas-api.hkemobility.gov.hk/tdas/api/route", {
|
|
169
144
|
method: "POST",
|
|
@@ -194,8 +169,11 @@ export async function fetchEstJourneyTime({
|
|
|
194
169
|
.map((v: string) => parseInt(v, 10));
|
|
195
170
|
return hh * 60 + mm;
|
|
196
171
|
})
|
|
197
|
-
.catch(() => {
|
|
198
|
-
|
|
172
|
+
.catch((e) => {
|
|
173
|
+
if (signal?.aborted) {
|
|
174
|
+
throw e;
|
|
175
|
+
}
|
|
176
|
+
// for any other error, assume 4 minutes journey time blindly
|
|
199
177
|
return 4;
|
|
200
178
|
})
|
|
201
179
|
.then((s) => {
|
|
@@ -206,8 +184,8 @@ export async function fetchEstJourneyTime({
|
|
|
206
184
|
);
|
|
207
185
|
}),
|
|
208
186
|
);
|
|
209
|
-
|
|
210
|
-
ret +=
|
|
187
|
+
minutes.forEach((m) => {
|
|
188
|
+
ret += m;
|
|
211
189
|
});
|
|
212
190
|
payloads.length = 0;
|
|
213
191
|
}
|