mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-02-23 07:09:35 +02:00
query live movements in an area
This commit is contained in:
parent
33417a6e61
commit
3fb0943680
7 changed files with 110 additions and 31 deletions
|
@ -8,5 +8,6 @@ module.exports = {
|
|||
address: require('./address'),
|
||||
poi: require('./poi'),
|
||||
location: require('./location'),
|
||||
locationFilter: require('./location-filter')
|
||||
locationFilter: require('./location-filter'),
|
||||
rectangle: require('./rectangle')
|
||||
}
|
||||
|
|
16
format/rectangle.js
Normal file
16
format/rectangle.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
'use strict'
|
||||
|
||||
const formatRectangle = (profile, north, west, south, east) => {
|
||||
return {
|
||||
llCrd: {
|
||||
x: profile.formatCoord(west),
|
||||
y: profile.formatCoord(south)
|
||||
},
|
||||
urCrd: {
|
||||
x: profile.formatCoord(east),
|
||||
y: profile.formatCoord(north)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = formatRectangle
|
43
index.js
43
index.js
|
@ -184,7 +184,48 @@ const createClient = (profile) => {
|
|||
})
|
||||
}
|
||||
|
||||
return {departures, journeys, locations, nearby, journeyPart}
|
||||
const radar = (north, west, south, east, opt) => {
|
||||
if ('number' !== typeof north) throw new Error('north must be a number.')
|
||||
if ('number' !== typeof west) throw new Error('west must be a number.')
|
||||
if ('number' !== typeof south) throw new Error('south must be a number.')
|
||||
if ('number' !== typeof east) throw new Error('east must be a number.')
|
||||
|
||||
opt = Object.assign({
|
||||
results: 256, // maximum number of vehicles
|
||||
duration: 30, // compute frames for the next n seconds
|
||||
frames: 3 // nr of frames to compute
|
||||
}, opt || {})
|
||||
opt.when = opt.when || new Date()
|
||||
|
||||
const durationPerStep = opt.duration / Math.max(opt.frames, 1) * 1000
|
||||
return request(profile, {
|
||||
meth: 'JourneyGeoPos',
|
||||
req: {
|
||||
maxJny: opt.results,
|
||||
onlyRT: false, // todo: does this mean "only realtime"?
|
||||
date: profile.formatDate(profile, opt.when),
|
||||
time: profile.formatTime(profile, opt.when),
|
||||
// todo: would a ring work here as well?
|
||||
rect: profile.formatRectangle(profile, north, west, south, east),
|
||||
perSize: opt.duration * 1000,
|
||||
perStep: Math.round(durationPerStep),
|
||||
ageOfReport: true, // todo: what is this?
|
||||
jnyFltrL: [
|
||||
// todo: use `profile.formatProducts(opt.products || {})`
|
||||
{type: 'PROD', mode: 'INC', value: '127'}
|
||||
],
|
||||
trainPosMode: 'CALC' // todo: what is this? what about realtime?
|
||||
}
|
||||
})
|
||||
.then((d) => {
|
||||
if (!Array.isArray(d.jnyL)) return []
|
||||
|
||||
const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks)
|
||||
return d.jnyL.map(parse)
|
||||
})
|
||||
}
|
||||
|
||||
return {departures, journeys, locations, nearby, journeyPart, radar}
|
||||
}
|
||||
|
||||
module.exports = createClient
|
||||
|
|
|
@ -21,6 +21,7 @@ const formatPoi = require('../format/poi')
|
|||
const formatStation = require('../format/station')
|
||||
const formatTime = require('../format/time')
|
||||
const formatLocation = require('../format/location')
|
||||
const formatRectangle = require('../format/rectangle')
|
||||
|
||||
const id = x => x
|
||||
|
||||
|
@ -52,7 +53,8 @@ const defaultProfile = {
|
|||
formatPoi,
|
||||
formatStation,
|
||||
formatTime,
|
||||
formatLocation
|
||||
formatLocation,
|
||||
formatRectangle
|
||||
}
|
||||
|
||||
module.exports = defaultProfile
|
||||
|
|
|
@ -27,7 +27,7 @@ const createParseMovement = (profile, locations, lines, remarks) => {
|
|||
}
|
||||
|
||||
const res = {
|
||||
direction: m.dirTxt,
|
||||
direction: profile.parseStationName(m.dirTxt),
|
||||
line: lines[m.prodX] || null,
|
||||
coordinates: m.pos ? {
|
||||
latitude: m.pos.y / 1000000,
|
||||
|
|
42
test/util.js
42
test/util.js
|
@ -3,31 +3,52 @@
|
|||
const isRoughlyEqual = require('is-roughly-equal')
|
||||
const floor = require('floordate')
|
||||
|
||||
const assertValidStation = (t, s) => {
|
||||
const assertValidStation = (t, s, coordsOptional = false) => {
|
||||
t.equal(typeof s.type, 'string')
|
||||
t.equal(s.type, 'station')
|
||||
t.equal(typeof s.id, 'string')
|
||||
|
||||
t.equal(typeof s.name, 'string')
|
||||
if (!coordsOptional) {
|
||||
if (!s.coordinates) console.trace()
|
||||
t.ok(s.coordinates)
|
||||
}
|
||||
if (s.coordinates) {
|
||||
t.equal(typeof s.coordinates.latitude, 'number')
|
||||
t.equal(typeof s.coordinates.longitude, 'number')
|
||||
}
|
||||
}
|
||||
|
||||
const assertValidPoi = (t, p) => {
|
||||
t.equal(typeof p.type, 'string')
|
||||
t.equal(p.type, 'poi')
|
||||
t.equal(typeof p.id, 'string')
|
||||
|
||||
t.equal(typeof p.name, 'string')
|
||||
t.ok(p.coordinates)
|
||||
if (s.coordinates) {
|
||||
t.equal(typeof p.coordinates.latitude, 'number')
|
||||
t.equal(typeof p.coordinates.longitude, 'number')
|
||||
}
|
||||
}
|
||||
|
||||
const assertValidAddress = (t, a) => {
|
||||
t.equal(typeof a.type, 'string')
|
||||
t.equal(a.type, 'address')
|
||||
|
||||
t.equal(typeof a.name, 'string')
|
||||
t.ok(a.coordinates)
|
||||
if (s.coordinates) {
|
||||
t.equal(typeof a.coordinates.latitude, 'number')
|
||||
t.equal(typeof a.coordinates.longitude, 'number')
|
||||
}
|
||||
}
|
||||
|
||||
const assertValidLocation = (t, l) => {
|
||||
t.equal(typeof l.type, 'string')
|
||||
if (l.type === 'station') assertValidStation(t, l)
|
||||
else if (l.type === 'poi') assertValidPoi(t, l)
|
||||
else if (l.type === 'address') assertValidAddress(t, l)
|
||||
else t.fail('invalid type ' + l.type)
|
||||
|
||||
t.equal(typeof l.name, 'string')
|
||||
t.ok(l.coordinates)
|
||||
t.equal(typeof l.coordinates.latitude, 'number')
|
||||
t.equal(typeof l.coordinates.longitude, 'number')
|
||||
}
|
||||
|
||||
const isValidMode = (m) => {
|
||||
|
@ -40,7 +61,6 @@ const isValidMode = (m) => {
|
|||
const assertValidLine = (t, l) => {
|
||||
t.equal(l.type, 'line')
|
||||
t.equal(typeof l.name, 'string')
|
||||
if (!isValidMode(l.mode)) console.error(l)
|
||||
t.ok(isValidMode(l.mode), 'invalid mode ' + l.mode)
|
||||
t.equal(typeof l.product, 'string')
|
||||
t.equal(l.public, true)
|
||||
|
@ -50,14 +70,14 @@ const isValidDateTime = (w) => {
|
|||
return !Number.isNaN(+new Date(w))
|
||||
}
|
||||
|
||||
const assertValidStopover = (t, s) => {
|
||||
const assertValidStopover = (t, s, coordsOptional = false) => {
|
||||
if ('arrival' in s) t.ok(isValidDateTime(s.arrival))
|
||||
if ('departure' in s) t.ok(isValidDateTime(s.departure))
|
||||
if (!('arrival' in s) && !('departure' in s)) {
|
||||
t.fail('stopover doesn\'t contain arrival or departure')
|
||||
}
|
||||
t.ok(s.station)
|
||||
assertValidStation(t, s.station)
|
||||
assertValidStation(t, s.station, coordsOptional)
|
||||
}
|
||||
|
||||
const minute = 60 * 1000
|
||||
|
@ -86,5 +106,5 @@ module.exports = {
|
|||
assertValidLine,
|
||||
isValidDateTime,
|
||||
assertValidStopover,
|
||||
when, isValidWhen, assertValidWhen
|
||||
hour, when, isValidWhen, assertValidWhen
|
||||
}
|
||||
|
|
31
test/vbb.js
31
test/vbb.js
|
@ -269,8 +269,7 @@ test('locations', async (t) => {
|
|||
|
||||
|
||||
|
||||
// todo
|
||||
test.skip('radar', async (t) => {
|
||||
test('radar', async (t) => {
|
||||
const vehicles = await client.radar(52.52411, 13.41002, 52.51942, 13.41709, {
|
||||
duration: 5 * 60, when
|
||||
})
|
||||
|
@ -290,21 +289,19 @@ test.skip('radar', async (t) => {
|
|||
t.ok(v.coordinates.longitude <= 15, 'vehicle is too far away')
|
||||
|
||||
t.ok(Array.isArray(v.nextStops))
|
||||
for (let s of v.nextStops) {
|
||||
assertValidFrameStation(t, s.station)
|
||||
if (!s.arrival && !s.departure)
|
||||
t.ifError(new Error('neither arrival nor departure return'))
|
||||
if (s.arrival) {
|
||||
t.equal(typeof s.arrival, 'string')
|
||||
const arr = +new Date(s.arrival)
|
||||
t.ok(!Number.isNaN(arr))
|
||||
for (let st of v.nextStops) {
|
||||
assertValidStopover(t, st, true)
|
||||
t.strictEqual(st.station.name.indexOf('(Berlin)'), -1)
|
||||
|
||||
if (st.arrival) {
|
||||
t.equal(typeof st.arrival, 'string')
|
||||
const arr = +new Date(st.arrival)
|
||||
// note that this can be an ICE train
|
||||
t.ok(isRoughlyEqual(14 * hour, +when, arr))
|
||||
}
|
||||
if (s.departure) {
|
||||
t.equal(typeof s.departure, 'string')
|
||||
const dep = +new Date(s.departure)
|
||||
t.ok(!Number.isNaN(dep))
|
||||
if (st.departure) {
|
||||
t.equal(typeof st.departure, 'string')
|
||||
const dep = +new Date(st.departure)
|
||||
// note that this can be an ICE train
|
||||
t.ok(isRoughlyEqual(14 * hour, +when, dep))
|
||||
}
|
||||
|
@ -312,8 +309,10 @@ test.skip('radar', async (t) => {
|
|||
|
||||
t.ok(Array.isArray(v.frames))
|
||||
for (let f of v.frames) {
|
||||
assertValidFrameStation(t, f.origin)
|
||||
assertValidFrameStation(t, f.destination)
|
||||
assertValidStation(t, f.origin, true)
|
||||
t.strictEqual(f.origin.name.indexOf('(Berlin)'), -1)
|
||||
assertValidStation(t, f.destination, true)
|
||||
t.strictEqual(f.destination.name.indexOf('(Berlin)'), -1)
|
||||
t.equal(typeof f.t, 'number')
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue