diff --git a/package.json b/package.json index 546888c9..9dd80ca3 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "devDependencies": { "co": "^4.6.0", "db-stations": "^1.25.0", + "is-coordinates": "^2.0.2", "is-roughly-equal": "^0.1.0", "tap-spec": "^4.1.1", "tape": "^4.8.0", diff --git a/parse/location.js b/parse/location.js index f692af91..1b7be2f3 100644 --- a/parse/location.js +++ b/parse/location.js @@ -1,25 +1,31 @@ 'use strict' -const types = Object.create(null) -types.P = 'poi' -types.S = 'station' -types.A = 'address' +const POI = 'P' +const STATION = 'S' +const ADDRESS = 'A' // todo: what is s.rRefL? // todo: is passing in profile necessary? const parseLocation = (profile, l) => { - const type = types[l.type] || 'unknown' - const res = { - type, - name: l.name, - coordinates: l.crd ? { - latitude: l.crd.y / 1000000, - longitude: l.crd.x / 1000000 - } : null + const res = {type: 'location'} + if (l.crd) { + res.latitude = l.crd.y / 1000000 + res.longitude = l.crd.x / 1000000 } - if (type === 'poi' || type === 'station') res.id = l.extId - if ('pCls' in l) res.products = profile.parseProducts(l.pCls) + if (l.type === STATION) { + const station = { + type: 'station', + id: l.extId, + location: res + } + if ('pCls' in l) station.products = profile.parseProducts(l.pCls) + return station + } + + if (type === POI) res.id = l.extId + else if (l.type === ADDRESS) res.address = l.name + else res.name = l.name return res } diff --git a/parse/movement.js b/parse/movement.js index 0cd4d1ba..f5b032ef 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -27,7 +27,8 @@ const createParseMovement = (profile, locations, lines, remarks) => { const res = { direction: profile.parseStationName(m.dirTxt), line: lines[m.prodX] || null, - coordinates: m.pos ? { + location: m.pos ? { + type: 'location', latitude: m.pos.y / 1000000, longitude: m.pos.x / 1000000 } : null, diff --git a/test/db.js b/test/db.js index aa4a9c12..b9634e45 100644 --- a/test/db.js +++ b/test/db.js @@ -53,18 +53,18 @@ const isJungfernheide = (s) => { return s.type === 'station' && (s.id === '008011167' || s.id === '8011167') && s.name === 'Berlin Jungfernheide' && - s.coordinates && - isRoughlyEqual(s.coordinates.latitude, 52.530408, .0005) && - isRoughlyEqual(s.coordinates.longitude, 13.299424, .0005) + s.location && + isRoughlyEqual(s.location.latitude, 52.530408, .0005) && + isRoughlyEqual(s.location.longitude, 13.299424, .0005) } const assertIsJungfernheide = (t, s) => { t.equal(s.type, 'station') t.ok(s.id === '008011167' || s.id === '8011167', 'id should be 8011167') t.equal(s.name, 'Berlin Jungfernheide') - t.ok(s.coordinates) - t.ok(isRoughlyEqual(s.coordinates.latitude, 52.530408, .0005)) - t.ok(isRoughlyEqual(s.coordinates.longitude, 13.299424, .0005)) + t.ok(s.location) + t.ok(isRoughlyEqual(s.location.latitude, 52.530408, .0005)) + t.ok(isRoughlyEqual(s.location.longitude, 13.299424, .0005)) } const assertValidProducts = (t, p) => { @@ -170,8 +170,8 @@ test('Berlin Jungfernheide to Torfstraße 17', co.wrap(function* (t) { const d = part.destination assertValidAddress(t, d) t.equal(d.name, 'Torfstraße 17') - t.ok(isRoughlyEqual(.0001, d.coordinates.latitude, 52.5416823)) - t.ok(isRoughlyEqual(.0001, d.coordinates.longitude, 13.3491223)) + t.ok(isRoughlyEqual(.0001, d.latitude, 52.5416823)) + t.ok(isRoughlyEqual(.0001, d.longitude, 13.3491223)) t.end() })) @@ -199,8 +199,8 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co.wrap(function* (t) { const d = part.destination assertValidPoi(t, d) t.equal(d.name, 'ATZE Musiktheater') - t.ok(isRoughlyEqual(.0001, d.coordinates.latitude, 52.542399)) - t.ok(isRoughlyEqual(.0001, d.coordinates.longitude, 13.350402)) + t.ok(isRoughlyEqual(.0001, d.latitude, 52.542399)) + t.ok(isRoughlyEqual(.0001, d.longitude, 13.350402)) t.end() })) diff --git a/test/util.js b/test/util.js index 2d7acaf2..1d9b2047 100644 --- a/test/util.js +++ b/test/util.js @@ -2,53 +2,57 @@ const isRoughlyEqual = require('is-roughly-equal') const {DateTime} = require('luxon') +const isValidWGS84 = require('is-coordinates') const assertValidStation = (t, s, coordsOptional = false) => { - t.equal(typeof s.type, 'string') t.equal(s.type, 'station') t.equal(typeof s.id, 'string') - + t.ok(s.id) 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') + t.ok(s.name) + + if (!coordsOptional || (s.location !== null && s.location !== undefined)) { + t.ok(s.location) + assertValidLocation(t, s.location, coordsOptional) } } 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 (p.coordinates) { - t.equal(typeof p.coordinates.latitude, 'number') - t.equal(typeof p.coordinates.longitude, 'number') - } + t.equal(typeof a.address, 'string') // todo: do POIs always have an address? + assertValidLocation(t, a, true) // todo: do POIs always have coords? } 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 (a.coordinates) { - t.equal(typeof a.coordinates.latitude, 'number') - t.equal(typeof a.coordinates.longitude, 'number') - } + t.equal(typeof a.address, 'string') + assertValidLocation(t, a, true) // todo: do addresses always have coords? } -const assertValidLocation = (t, l) => { - 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) +const assertValidLocation = (t, l, coordsOptional = false) => { + t.equal(l.type, 'location') + if (l.name !== null && l.name !== undefined) { + t.equal(typeof l.name, 'string') + t.ok(l.name) + } + + if (l.address !== null && l.address !== undefined) { + t.equal(typeof l.address, 'string') + t.ok(l.address) + } + + const hasLatitude = l.latitude !== null && l.latitude !== undefined + const hasLongitude = l.longitude !== null && l.longitude !== undefined + if (!coordsOptional && hasLatitude) t.equal(typeof l.latitude, 'number') + if (!coordsOptional && hasLongitude) t.equal(typeof l.longitude, 'number') + if ((hasLongitude && !hasLatitude) || (hasLatitude && !hasLongitude)) { + t.fail('should have both .latitude and .longitude') + } + if (hasLatitude && hasLongitude) isValidWGS84([l.longitude, l.latitude]) + + if (!coordsOptional && l.altitude !== null && l.altitude !== undefined) { + t.equal(typeof l.altitude, 'number') + } } const isValidMode = (m) => { diff --git a/test/vbb.js b/test/vbb.js index 695a9199..2bbf1ac7 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -204,8 +204,8 @@ test('journeys – station to address', co.wrap(function* (t) { const dest = part.destination assertValidAddress(t, dest) t.strictEqual(dest.name, 'Torfstr. 17') - t.ok(isRoughlyEqual(.0001, dest.coordinates.latitude, 52.5416823)) - t.ok(isRoughlyEqual(.0001, dest.coordinates.longitude, 13.3491223)) + t.ok(isRoughlyEqual(.0001, dest.latitude, 52.5416823)) + t.ok(isRoughlyEqual(.0001, dest.longitude, 13.3491223)) assertValidWhen(t, part.arrival) t.end() @@ -231,8 +231,8 @@ test('journeys – station to POI', co.wrap(function* (t) { const dest = part.destination assertValidPoi(t, dest) t.strictEqual(dest.name, 'ATZE Musiktheater') - t.ok(isRoughlyEqual(.0001, dest.coordinates.latitude, 52.543333)) - t.ok(isRoughlyEqual(.0001, dest.coordinates.longitude, 13.351686)) + t.ok(isRoughlyEqual(.0001, dest.latitude, 52.543333)) + t.ok(isRoughlyEqual(.0001, dest.longitude, 13.351686)) assertValidWhen(t, part.arrival) t.end() @@ -322,12 +322,12 @@ test('radar', co.wrap(function* (t) { t.ok(findStation(v.direction)) assertValidLine(t, v.line) - t.equal(typeof v.coordinates.latitude, 'number') - t.ok(v.coordinates.latitude <= 55, 'vehicle is too far away') - t.ok(v.coordinates.latitude >= 45, 'vehicle is too far away') - t.equal(typeof v.coordinates.longitude, 'number') - t.ok(v.coordinates.longitude >= 9, 'vehicle is too far away') - t.ok(v.coordinates.longitude <= 15, 'vehicle is too far away') + t.equal(typeof v.location.latitude, 'number') + t.ok(v.location.latitude <= 55, 'vehicle is too far away') + t.ok(v.location.latitude >= 45, 'vehicle is too far away') + t.equal(typeof v.location.longitude, 'number') + t.ok(v.location.longitude >= 9, 'vehicle is too far away') + t.ok(v.location.longitude <= 15, 'vehicle is too far away') t.ok(Array.isArray(v.nextStops)) for (let st of v.nextStops) {