hk-bus-eta 3.5.0 → 3.5.2
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.js +62 -31
- package/dist/journeyTime.js.map +1 -1
- package/esm/journeyTime.js +62 -31
- package/esm/journeyTime.js.map +1 -1
- package/package.json +1 -1
- package/src/journeyTime.ts +61 -21
package/dist/journeyTime.js
CHANGED
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.fetchEstJourneyTime = void 0;
|
|
13
|
+
const __HK_BUS_ETA_JT_CACHE__ = {};
|
|
13
14
|
/**
|
|
14
15
|
* Fetch Journey time in minute for a route
|
|
15
16
|
* @param {Object}
|
|
@@ -26,49 +27,79 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
|
|
|
26
27
|
throw new Error("endSeq should be < number of stops");
|
|
27
28
|
if (startSeq >= endSeq)
|
|
28
29
|
throw new Error("startSeq should be < endSeq");
|
|
30
|
+
let ts = Date.now();
|
|
29
31
|
let ret = 0;
|
|
30
32
|
let payloads = [];
|
|
31
33
|
for (let i = startSeq; i < endSeq; ++i) {
|
|
32
|
-
payloads.push(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
payloads.push([
|
|
35
|
+
`${stops[i]}-${stops[i + 1]}`,
|
|
36
|
+
JSON.stringify({
|
|
37
|
+
start: {
|
|
38
|
+
lat: stopList[stops[i]].location.lat,
|
|
39
|
+
long: stopList[stops[i]].location.lng,
|
|
40
|
+
},
|
|
41
|
+
end: {
|
|
42
|
+
lat: stopList[stops[i + 1]].location.lat,
|
|
43
|
+
long: stopList[stops[i + 1]].location.lng,
|
|
44
|
+
},
|
|
45
|
+
departIn: Math.round(ret / 15) * 15
|
|
46
|
+
})
|
|
47
|
+
]);
|
|
43
48
|
if (payloads.length < batchSize && i !== endSeq - 1) {
|
|
44
49
|
// skip fetching until whole batch filled
|
|
45
50
|
continue;
|
|
46
51
|
}
|
|
47
|
-
const minutes = yield Promise.all(payloads.map(payload =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
const minutes = yield Promise.all(payloads.map(([key, payload]) => {
|
|
53
|
+
// load from cache if it is query within 15 minutes
|
|
54
|
+
if (key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
|
|
55
|
+
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m);
|
|
56
|
+
}
|
|
57
|
+
return (
|
|
58
|
+
//TODO: update to use gov API
|
|
59
|
+
// fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
60
|
+
fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': "application/json",
|
|
64
|
+
},
|
|
65
|
+
body: payload,
|
|
66
|
+
signal,
|
|
67
|
+
})
|
|
68
|
+
.then(r => r.json())
|
|
69
|
+
.then(({ eta, distM, jSpeed }) => {
|
|
70
|
+
var _a;
|
|
71
|
+
const speedStr = (_a = /(\d+)公里\/小時/g.exec(jSpeed)) === null || _a === void 0 ? void 0 : _a[1];
|
|
72
|
+
if (speedStr !== null &&
|
|
73
|
+
speedStr !== undefined &&
|
|
74
|
+
!isNaN(parseInt(speedStr, 10))) {
|
|
75
|
+
// Set the speed limit as 70 km/h
|
|
76
|
+
return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
|
|
77
|
+
}
|
|
78
|
+
console.warn("Unable to parse TDAS response for more precise journey time. Falling back.");
|
|
79
|
+
const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
|
|
80
|
+
return hh * 60 + mm;
|
|
81
|
+
})
|
|
82
|
+
.catch(() => {
|
|
83
|
+
// for any error, assume 4 minutes journey time blindly
|
|
84
|
+
return 4;
|
|
85
|
+
})
|
|
86
|
+
.then(m => {
|
|
87
|
+
__HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts };
|
|
88
|
+
// margin for car accelerating, decelerating, and passenger picking up and dropping off
|
|
89
|
+
return m + 1;
|
|
90
|
+
}));
|
|
91
|
+
}));
|
|
67
92
|
minutes.forEach(m => {
|
|
68
93
|
ret += m;
|
|
69
94
|
});
|
|
70
95
|
payloads.length = 0;
|
|
71
96
|
}
|
|
97
|
+
for (const k in __HK_BUS_ETA_JT_CACHE__) {
|
|
98
|
+
// clean up cache
|
|
99
|
+
if (__HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts) {
|
|
100
|
+
delete __HK_BUS_ETA_JT_CACHE__[k];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
72
103
|
return ret;
|
|
73
104
|
});
|
|
74
105
|
}
|
package/dist/journeyTime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
|
package/esm/journeyTime.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const __HK_BUS_ETA_JT_CACHE__ = {};
|
|
1
2
|
/**
|
|
2
3
|
* Fetch Journey time in minute for a route
|
|
3
4
|
* @param {Object}
|
|
@@ -13,49 +14,79 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
|
|
|
13
14
|
throw new Error("endSeq should be < number of stops");
|
|
14
15
|
if (startSeq >= endSeq)
|
|
15
16
|
throw new Error("startSeq should be < endSeq");
|
|
17
|
+
let ts = Date.now();
|
|
16
18
|
let ret = 0;
|
|
17
19
|
let payloads = [];
|
|
18
20
|
for (let i = startSeq; i < endSeq; ++i) {
|
|
19
|
-
payloads.push(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
payloads.push([
|
|
22
|
+
`${stops[i]}-${stops[i + 1]}`,
|
|
23
|
+
JSON.stringify({
|
|
24
|
+
start: {
|
|
25
|
+
lat: stopList[stops[i]].location.lat,
|
|
26
|
+
long: stopList[stops[i]].location.lng,
|
|
27
|
+
},
|
|
28
|
+
end: {
|
|
29
|
+
lat: stopList[stops[i + 1]].location.lat,
|
|
30
|
+
long: stopList[stops[i + 1]].location.lng,
|
|
31
|
+
},
|
|
32
|
+
departIn: Math.round(ret / 15) * 15
|
|
33
|
+
})
|
|
34
|
+
]);
|
|
30
35
|
if (payloads.length < batchSize && i !== endSeq - 1) {
|
|
31
36
|
// skip fetching until whole batch filled
|
|
32
37
|
continue;
|
|
33
38
|
}
|
|
34
|
-
const minutes = await Promise.all(payloads.map(payload =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
const minutes = await Promise.all(payloads.map(([key, payload]) => {
|
|
40
|
+
// load from cache if it is query within 15 minutes
|
|
41
|
+
if (key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
|
|
42
|
+
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m);
|
|
43
|
+
}
|
|
44
|
+
return (
|
|
45
|
+
//TODO: update to use gov API
|
|
46
|
+
// fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
47
|
+
fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': "application/json",
|
|
51
|
+
},
|
|
52
|
+
body: payload,
|
|
53
|
+
signal,
|
|
54
|
+
})
|
|
55
|
+
.then(r => r.json())
|
|
56
|
+
.then(({ eta, distM, jSpeed }) => {
|
|
57
|
+
var _a;
|
|
58
|
+
const speedStr = (_a = /(\d+)公里\/小時/g.exec(jSpeed)) === null || _a === void 0 ? void 0 : _a[1];
|
|
59
|
+
if (speedStr !== null &&
|
|
60
|
+
speedStr !== undefined &&
|
|
61
|
+
!isNaN(parseInt(speedStr, 10))) {
|
|
62
|
+
// Set the speed limit as 70 km/h
|
|
63
|
+
return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
|
|
64
|
+
}
|
|
65
|
+
console.warn("Unable to parse TDAS response for more precise journey time. Falling back.");
|
|
66
|
+
const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
|
|
67
|
+
return hh * 60 + mm;
|
|
68
|
+
})
|
|
69
|
+
.catch(() => {
|
|
70
|
+
// for any error, assume 4 minutes journey time blindly
|
|
71
|
+
return 4;
|
|
72
|
+
})
|
|
73
|
+
.then(m => {
|
|
74
|
+
__HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts };
|
|
75
|
+
// margin for car accelerating, decelerating, and passenger picking up and dropping off
|
|
76
|
+
return m + 1;
|
|
77
|
+
}));
|
|
78
|
+
}));
|
|
54
79
|
minutes.forEach(m => {
|
|
55
80
|
ret += m;
|
|
56
81
|
});
|
|
57
82
|
payloads.length = 0;
|
|
58
83
|
}
|
|
84
|
+
for (const k in __HK_BUS_ETA_JT_CACHE__) {
|
|
85
|
+
// clean up cache
|
|
86
|
+
if (__HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts) {
|
|
87
|
+
delete __HK_BUS_ETA_JT_CACHE__[k];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
59
90
|
return ret;
|
|
60
91
|
}
|
|
61
92
|
//# sourceMappingURL=journeyTime.js.map
|
package/esm/journeyTime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/journeyTime.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { RouteListEntry, StopList } from "./type";
|
|
2
2
|
|
|
3
|
+
type JT_CACHE = Record<string, {
|
|
4
|
+
m: number;
|
|
5
|
+
ts: number;
|
|
6
|
+
}>
|
|
7
|
+
|
|
8
|
+
const __HK_BUS_ETA_JT_CACHE__: JT_CACHE = {}
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* Fetch Journey time in minute for a route
|
|
5
12
|
* @param {Object}
|
|
@@ -17,10 +24,12 @@ export async function fetchEstJourneyTime({
|
|
|
17
24
|
if ( endSeq >= stops.length ) throw new Error("endSeq should be < number of stops")
|
|
18
25
|
if ( startSeq >= endSeq ) throw new Error("startSeq should be < endSeq")
|
|
19
26
|
|
|
27
|
+
let ts = Date.now()
|
|
20
28
|
let ret = 0
|
|
21
29
|
let payloads = []
|
|
22
30
|
for ( let i = startSeq; i < endSeq; ++i ) {
|
|
23
|
-
payloads.push(
|
|
31
|
+
payloads.push([
|
|
32
|
+
`${stops[i]}-${stops[i+1]}`,
|
|
24
33
|
JSON.stringify({
|
|
25
34
|
start: {
|
|
26
35
|
lat: stopList[stops[i]].location.lat,
|
|
@@ -32,36 +41,67 @@ export async function fetchEstJourneyTime({
|
|
|
32
41
|
},
|
|
33
42
|
departIn: Math.round(ret / 15) * 15
|
|
34
43
|
})
|
|
35
|
-
)
|
|
44
|
+
])
|
|
36
45
|
if ( payloads.length < batchSize && i !== endSeq - 1 ) {
|
|
37
46
|
// skip fetching until whole batch filled
|
|
38
47
|
continue
|
|
39
48
|
}
|
|
40
|
-
const minutes = await Promise.all(payloads.map(payload =>
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.catch(() => {
|
|
57
|
-
// for any error, assume 5 minutes travel time blindly
|
|
58
|
-
return 5;
|
|
49
|
+
const minutes = await Promise.all(payloads.map(([key, payload]) => {
|
|
50
|
+
// load from cache if it is query within 15 minutes
|
|
51
|
+
if ( key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts ) {
|
|
52
|
+
return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
//TODO: update to use gov API
|
|
57
|
+
// fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
58
|
+
fetch('https://tdas.hkbus.app/tdas/api/route', {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': "application/json",
|
|
62
|
+
},
|
|
63
|
+
body: payload,
|
|
64
|
+
signal,
|
|
59
65
|
})
|
|
66
|
+
.then(r => r.json())
|
|
67
|
+
.then(({eta, distM, jSpeed}) => {
|
|
68
|
+
const speedStr = /(\d+)公里\/小時/g.exec(jSpeed)?.[1];
|
|
69
|
+
if (
|
|
70
|
+
speedStr !== null &&
|
|
71
|
+
speedStr !== undefined &&
|
|
72
|
+
!isNaN(parseInt(speedStr, 10))
|
|
73
|
+
) {
|
|
74
|
+
// Set the speed limit as 70 km/h
|
|
75
|
+
return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
|
|
76
|
+
}
|
|
77
|
+
console.warn(
|
|
78
|
+
"Unable to parse TDAS response for more precise journey time. Falling back."
|
|
79
|
+
);
|
|
80
|
+
const [hh, mm] = eta.split(":").map((v: string) => parseInt(v, 10))
|
|
81
|
+
return hh * 60 + mm;
|
|
82
|
+
})
|
|
83
|
+
.catch(() => {
|
|
84
|
+
// for any error, assume 4 minutes journey time blindly
|
|
85
|
+
return 4;
|
|
86
|
+
})
|
|
87
|
+
.then(m => {
|
|
88
|
+
__HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts }
|
|
89
|
+
// margin for car accelerating, decelerating, and passenger picking up and dropping off
|
|
90
|
+
return m + 1
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
}
|
|
60
94
|
))
|
|
61
95
|
minutes.forEach(m => {
|
|
62
96
|
ret += m
|
|
63
97
|
})
|
|
64
98
|
payloads.length = 0
|
|
65
99
|
}
|
|
100
|
+
for ( const k in __HK_BUS_ETA_JT_CACHE__ ) {
|
|
101
|
+
// clean up cache
|
|
102
|
+
if ( __HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts ) {
|
|
103
|
+
delete __HK_BUS_ETA_JT_CACHE__[k];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
66
106
|
return ret
|
|
67
107
|
}
|