From f37728c35bb3527185b404a65f16cd05f5b30b77 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 19 Mar 2018 22:00:25 +0100 Subject: [PATCH 01/33] writing a profile guide: runkit permalink --- docs/writing-a-profile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 1aca3f2e..4e376911 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -116,7 +116,7 @@ Let's break this down: todo: `defaultProducts`, `allProducts`, `bitmasks`, add to profile -If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. +If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.1.0) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. ### Finding the right values for the `bitmask` field From e71908ed1c45dbba659dafc7e97b3da9490bbfe8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 19 Mar 2018 22:19:45 +0100 Subject: [PATCH 02/33] changelog for 2.4.x --- docs/changelog.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 6b5bc0a1..64ee417d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,3 +4,19 @@ - new [Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) [profile](../p/nahsh) - new [*writing a profile* guide](./writing-a-profile.md) + +## `2.4.2` + +- `parseStopover`: expose canceled arrivals & departures 🐛 + +## `2.4.1` + +- new [*writing a profile* guide](./writing-a-profile.md) +- `parseMovement`: use `parseStopover` 🐛 +- `parseStopover`: use `parseStationName` 🐛 + +## `2.4.0` + +- new [Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt)/[INSA](https://insa.de) profile +- new `earlierRef`/`laterRef` feature to query earlier/later journeys (pagination) +- former scheduled date & time for canceled departures & journeys From 98139f92109bd4ed77dc53457665e0e0f47ff755 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 19 Mar 2018 22:34:08 +0100 Subject: [PATCH 03/33] prepare move to public-transport org --- docs/writing-a-profile.md | 12 ++++++------ index.js | 2 +- lib/request.js | 2 +- package.json | 6 +++--- parse/departure.js | 4 ++-- readme.md | 6 +++--- test/oebb.js | 2 +- test/validate-line-without-mode.js | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 4e376911..e44a8d0f 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -8,7 +8,7 @@ This guide is about writing such a profile. If you just want to use an already supported endpoint, refer to the [API documentation](readme.md) instead. -*Note*: **If you get stuck, ask for help by [creating an issue](https://github.com/derhuerst/hafas-client/issues/new)!** We want to help people expand the scope of this library. +*Note*: **If you get stuck, ask for help by [creating an issue](https://github.com/public-transport/hafas-client/issues/new)!** We want to help people expand the scope of this library. ## 0. How do the profiles work? @@ -61,7 +61,7 @@ If you pass this profile into `hafas-client`, the `parseLine` method will overri 1. **Get an iOS or Android device and download the "official" app** for the public transport provider that you want to build a profile for. 2. **Configure a [man-in-the-middle HTTP proxy](https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/)** like [mitmproxy](https://mitmproxy.org). - Configure your device to trust the self-signed SSL certificate, [as outlined in the mitmproxy docs](https://docs.mitmproxy.org/stable/concepts-certificates/). - - *Note*: This method does not work if the app uses [public key pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning). In this case (the app won't be able to query data), please [create an issue](https://github.com/derhuerst/hafas-client/issues/new), so we can discuss other techniques. + - *Note*: This method does not work if the app uses [public key pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning). In this case (the app won't be able to query data), please [create an issue](https://github.com/public-transport/hafas-client/issues/new), so we can discuss other techniques. 3. **Record requests of the app.** - [There's a video showing this step](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4). - Make sure to cover all relevant sections of the app, e.g. "journeys", "departures", "live map". Better record more than less; You will regret not having enough information later on. @@ -74,7 +74,7 @@ If you pass this profile into `hafas-client`, the `parseLine` method will overri - **Identify the `locale`.** Basically guess work; Use the date & time formats as an indicator. - **Identify the `timezone`.** This may be tricky, a for example [Deutsche Bahn](https://en.wikipedia.org/wiki/Deutsche_Bahn) returns departures for Moscow as `+01:00` instead of `+03:00`. - **Copy the authentication** and other meta fields, namely `ver`, `ext`, `client` and `lang`. - - You can find these fields in the root of each request JSON. Check [a VBB request](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L13-L22) and [the corresponding VBB profile](https://github.com/derhuerst/hafas-client/blob/6e61097687a37b60d53e767f2711466b80c5142c/p/vbb/index.js#L22-L29) for an example. + - You can find these fields in the root of each request JSON. Check [a VBB request](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L13-L22) and [the corresponding VBB profile](https://github.com/public-transport/hafas-client/blob/6e61097687a37b60d53e767f2711466b80c5142c/p/vbb/index.js#L22-L29) for an example. - Add a function `transformReqBody(body)` to your profile, which assigns them to `body`. - Some profiles have a `checksum` parameter (like [here](https://gist.github.com/derhuerst/2a735268bd82a0a6779633f15dceba33#file-journey-details-1-http-L1)) or two `mic` & `mac` parameters (like [here](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L1)). If you see one of them in your requests, jump to [*Appendix A: checksum, mic, mac*](#appendix-a-checksum-mic-mac). Unfortunately, this is necessary to get the profile working. @@ -116,7 +116,7 @@ Let's break this down: todo: `defaultProducts`, `allProducts`, `bitmasks`, add to profile -If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.1.0) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. +If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/public-transport/hafas-client-profile-example/0.1.0) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. ### Finding the right values for the `bitmask` field @@ -157,10 +157,10 @@ You can just query these, as long as you send a formally correct request. ### endpoints using the `checksum` query parameter -`checksum` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code): `hafas-client` will compute it by [hashing](https://en.wikipedia.org/wiki/Hash_function) the request body and a secret *salt*. **This secret can be read from the config file inside the app bundle.** There is no guide for this yet, so please [open an issue](https://github.com/derhuerst/hafas-client/issues/new) instead. +`checksum` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code): `hafas-client` will compute it by [hashing](https://en.wikipedia.org/wiki/Hash_function) the request body and a secret *salt*. **This secret can be read from the config file inside the app bundle.** There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new) instead. ### endpoints using the `mic` & `mac` query parameters `mic` is a [message integrity code](https://en.wikipedia.org/wiki/Message_authentication_code), the [hash](https://en.wikipedia.org/wiki/Hash_function) of the request body. -`mac` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code), the hash of `mic` and a secret *salt*. **This secret can be read from the config file inside the app bundle.** There is no guide for this yet, so please [open an issue](https://github.com/derhuerst/hafas-client/issues/new) instead. +`mac` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code), the hash of `mic` and a secret *salt*. **This secret can be read from the config file inside the app bundle.** There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new) instead. diff --git a/index.js b/index.js index 158d0151..374c1aa7 100644 --- a/index.js +++ b/index.js @@ -105,7 +105,7 @@ const createClient = (profile, request = _request) => { // `CGI_READ_FAILED` if you pass `numF`, the parameter for the number // of results. To circumvent this, we loop here, collecting journeys // until we have enough. - // see https://github.com/derhuerst/hafas-client/pull/23#issuecomment-370246163 + // see https://github.com/public-transport/hafas-client/pull/23#issuecomment-370246163 // todo: check if `numF` is supported again, revert this change const journeys = [] const more = (when, journeysRef) => { diff --git a/lib/request.js b/lib/request.js index 96d5da5f..839a3e4d 100644 --- a/lib/request.js +++ b/lib/request.js @@ -20,7 +20,7 @@ const request = (profile, data) => { headers: { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', - 'user-agent': 'https://github.com/derhuerst/hafas-client' + 'user-agent': 'https://github.com/public-transport/hafas-client' }, query: {} }) diff --git a/package.json b/package.json index 90938153..c82a47f6 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,9 @@ "contributors": [ "Julius Tens " ], - "homepage": "https://github.com/derhuerst/hafas-client", - "repository": "derhuerst/hafas-client", - "bugs": "https://github.com/derhuerst/hafas-client/issues", + "homepage": "https://github.com/public-transport/hafas-client", + "repository": "public-transport/hafas-client", + "bugs": "https://github.com/public-transport/hafas-client/issues", "license": "ISC", "keywords": [ "hafas", diff --git a/parse/departure.js b/parse/departure.js index 3868c988..158eb1ea 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -1,10 +1,10 @@ 'use strict' -// todos from derhuerst/hafas-client#2 +// todos from public-transport/hafas-client#2 // - stdStop.dPlatfS, stdStop.dPlatfR // todo: what is d.jny.dirFlg? // todo: d.stbStop.dProgType -// todo: d.freq, d.freq.jnyL, see https://github.com/derhuerst/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 +// todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 const createParseDeparture = (profile, stations, lines, remarks) => { const findRemark = rm => remarks[parseInt(rm.remX)] || null diff --git a/readme.md b/readme.md index d12d8df5..fb215256 100644 --- a/readme.md +++ b/readme.md @@ -11,8 +11,8 @@ HAFAS endpoint | wrapper library | docs | example code | source code [Nahverkehrsverbund Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) | [`nahsh-hafas`](https://github.com/juliuste/nahsh-hafas) | [docs](p/nahsh/readme.md) | [example code](p/nahsh/example.js) | [src](p/nahsh/index.js) [![npm version](https://img.shields.io/npm/v/hafas-client.svg)](https://www.npmjs.com/package/hafas-client) -[![build status](https://img.shields.io/travis/derhuerst/hafas-client.svg)](https://travis-ci.org/derhuerst/hafas-client) -![ISC-licensed](https://img.shields.io/github/license/derhuerst/hafas-client.svg) +[![build status](https://img.shields.io/travis/public-transport/hafas-client.svg)](https://travis-ci.org/public-transport/hafas-client) +![ISC-licensed](https://img.shields.io/github/license/public-transport/hafas-client.svg) [![chat on gitter](https://badges.gitter.im/derhuerst.svg)](https://gitter.im/derhuerst) [![support me on Patreon](https://img.shields.io/badge/support%20me-on%20patreon-fa7664.svg)](https://patreon.com/derhuerst) @@ -181,4 +181,4 @@ The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript ## Contributing -If you **have a question**, **found a bug** or want to **propose a feature**, have a look at [the issues page](https://github.com/derhuerst/hafas-client/issues). +If you **have a question**, **found a bug** or want to **propose a feature**, have a look at [the issues page](https://github.com/public-transport/hafas-client/issues). diff --git a/test/oebb.js b/test/oebb.js index 7427b6db..f9ee62d9 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -95,7 +95,7 @@ const assertValidPrice = (t, p) => { } // todo: fix this upstream -// see https://github.com/derhuerst/hafas-client/blob/c6e558be217667f1bcdac4a605898eb75ea80374/p/oebb/products.js#L71 +// see https://github.com/public-transport/hafas-client/blob/c6e558be217667f1bcdac4a605898eb75ea80374/p/oebb/products.js#L71 const assertValidLine = (t, l) => { // with optional mode const validators = Object.assign({}, validateFptf.defaultValidators, { line: validateLineWithoutMode diff --git a/test/validate-line-without-mode.js b/test/validate-line-without-mode.js index 71068e32..8f916348 100644 --- a/test/validate-line-without-mode.js +++ b/test/validate-line-without-mode.js @@ -19,7 +19,7 @@ const validateLineWithoutMode = (validate, line, name) => { a.ok(line.name.length > 0, name + '.name can\'t be empty') // skipping line validation here - // see https://github.com/derhuerst/hafas-client/issues/8#issuecomment-355839965 + // see https://github.com/public-transport/hafas-client/issues/8#issuecomment-355839965 if (is.undefined(line.mode) || is.null(line.mode)) { console.error(`ÖBB: Missing \`mode\` for line ${line.name} (at ${name}).`) } From cfd79b1be15a851172045ba6f266b7a7c2b2fd9a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 21 Mar 2018 01:14:37 +0100 Subject: [PATCH 04/33] parseJourney: use parseJourneyLeg from profile :bug: --- parse/journey.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/parse/journey.js b/parse/journey.js index b0a133e9..db72473c 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -1,11 +1,9 @@ 'use strict' -const createParseJourneyLeg = require('./journey-leg') - const clone = obj => Object.assign({}, obj) const createParseJourney = (profile, stations, lines, remarks) => { - const parseLeg = createParseJourneyLeg(profile, stations, lines, remarks) + const parseLeg = profile.parseJourneyLeg(profile, stations, lines, remarks) // todo: c.sDays // todo: c.dep.dProgType, c.arr.dProgType From 261cc5ed2c32145231786e58d0f9c9d00a671cd3 Mon Sep 17 00:00:00 2001 From: Julius Tens Date: Sun, 25 Mar 2018 21:34:50 +0200 Subject: [PATCH 05/33] add link to insa-hafas --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index fb215256..d5b042ef 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@ HAFAS endpoint | wrapper library | docs | example code | source code [Deutsche Bahn (DB)](https://en.wikipedia.org/wiki/Deutsche_Bahn) | [`db-hafas`](https://github.com/derhuerst/db-hafas) | [docs](p/db/readme.md) | [example code](p/db/example.js) | [src](p/db/index.js) [Berlin & Brandenburg public transport (VBB)](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) | [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas) | [docs](p/vbb/readme.md) | [example code](p/vbb/example.js) | [src](p/vbb/index.js) [Österreichische Bundesbahnen (ÖBB)](https://en.wikipedia.org/wiki/Austrian_Federal_Railways) | [`oebb-hafas`](https://github.com/juliuste/oebb-hafas) | [docs](p/oebb/readme.md) | [example code](p/oebb/example.js) | [src](p/oebb/index.js) -[Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt)/[INSA](https://insa.de) | – | [docs](p/insa/readme.md) | [example code](p/insa/example.js) | [src](p/insa/index.js) +[Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt)/[INSA](https://insa.de) | [`insa-hafas`](https://github.com/derhuerst/insa-hafas) | [docs](p/insa/readme.md) | [example code](p/insa/example.js) | [src](p/insa/index.js) [Nahverkehrsverbund Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) | [`nahsh-hafas`](https://github.com/juliuste/nahsh-hafas) | [docs](p/nahsh/readme.md) | [example code](p/nahsh/example.js) | [src](p/nahsh/index.js) [![npm version](https://img.shields.io/npm/v/hafas-client.svg)](https://www.npmjs.com/package/hafas-client) From afc01249821cfe80581d2b59259b9100063e4e4e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Apr 2018 14:23:14 +0200 Subject: [PATCH 06/33] fix stopover parsing :bug: --- parse/stopover.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parse/stopover.js b/parse/stopover.js index 2a445bd6..d96a6498 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -26,12 +26,12 @@ const createParseStopover = (profile, stations, lines, remarks, date) => { Object.defineProperty(res, 'canceled', {value: true}) if (st.aCncl) { res.arrival = res.arrivalDelay = null - const arr = profile.parseDateTime(profile, d.date, st.aTimeS) + const arr = profile.parseDateTime(profile, date, st.aTimeS) res.formerScheduledArrival = arr.toISO() } if (st.dCncl) { res.departure = res.departureDelay = null - const arr = profile.parseDateTime(profile, d.date, st.dTimeS) + const arr = profile.parseDateTime(profile, date, st.dTimeS) res.formerScheduledDeparture = arr.toISO() } } From c8a144427e011e54a69927604bdc02d34b947aef Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Apr 2018 14:23:27 +0200 Subject: [PATCH 07/33] 2.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c82a47f6..1d74ec75 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.5.0", + "version": "2.5.1", "main": "index.js", "files": [ "index.js", From 16e6dd6d1441cb2b528a955736702efd7a15754e Mon Sep 17 00:00:00 2001 From: Julian Amelung Date: Tue, 17 Apr 2018 17:58:55 +0200 Subject: [PATCH 08/33] Changed Journeys to Departures. the endpoint which is discussed here is departures not journeys. If you use journeys with this parameters, you get mad with debugging. --- docs/departures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/departures.md b/docs/departures.md index 6438b986..1a16535a 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -44,7 +44,7 @@ const vbbProfile = require('hafas-client/p/vbb') const client = createClient(vbbProfile) // S Charlottenburg -client.journeys('900000024101', {duration: 3}) +client.departures('900000024101', {duration: 3}) .then(console.log) .catch(console.error) ``` From 3d69ae225d9fb4807c964abce7284a6cc8bf5d06 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 16:59:35 +0200 Subject: [PATCH 09/33] DB: tram mode should be `train` :bug: --- p/db/products.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/db/products.js b/p/db/products.js index fa683e2b..3381f401 100644 --- a/p/db/products.js +++ b/p/db/products.js @@ -68,7 +68,7 @@ module.exports = [ }, { id: 'tram', - mode: 'tram', + mode: 'train', bitmasks: [256], name: 'Tram', short: 'T', From c60213a135322f5cfff7d78256562ce07f53d4a1 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 17:01:25 +0200 Subject: [PATCH 10/33] DB: tram mode should be `train` :bug: --- p/db/modes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/db/modes.js b/p/db/modes.js index dcca851f..d8e872b7 100644 --- a/p/db/modes.js +++ b/p/db/modes.js @@ -62,7 +62,7 @@ const m = { bitmask: 256, name: 'Tram', short: 'T', - mode: 'tram', + mode: 'train', product: 'tram' }, taxi: { From c030724663d40562c3ecabab1b11f7365a43c673 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 17:01:39 +0200 Subject: [PATCH 11/33] 2.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d74ec75..9629243c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.5.1", + "version": "2.5.2", "main": "index.js", "files": [ "index.js", From d676b8430d550b37367b3a8435fb2462dcfde32b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 29 Apr 2018 13:41:21 +0200 Subject: [PATCH 12/33] fix journey leg alternatives :bug: - use realtime data if available - don't filter by departure station (HAFAS IDs are complicated) --- parse/journey-leg.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 30477270..8e7b054a 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -58,15 +58,15 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => { if (pt.jny.freq && pt.jny.freq.jnyL) { const parseAlternative = (a) => { - const t = a.stopL[0].dTimeS || a.stopL[0].dTimeR + const t = a.stopL[0].dTimeR || a.stopL[0].dTimeS const when = profile.parseDateTime(profile, j.date, t) + // todo: expose a.stopL[0] return { line: lines[parseInt(a.prodX)] || null, when: when.toISO() } } res.alternatives = pt.jny.freq.jnyL - .filter(a => a.stopL[0].locX === pt.dep.locX) .map(parseAlternative) } } From 3c052e9fef4640ed4259c27f167525848bf4dff7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 29 Apr 2018 14:20:03 +0200 Subject: [PATCH 13/33] 2.5.3 --- docs/changelog.md | 13 +++++++++++++ package.json | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 64ee417d..117cf59c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,18 @@ # Changelog +## `2.5.3` + +- d676b84 fix parsing for journey leg alternatives 🐛 + +## `2.5.2` + +- 16e6dd6 departure docs: fix method 📝 +- c60213a DB: tram mode should be `train` 🐛 + +## `2.5.1` + +- afc0124 fix stopover parsing 🐛 + ## `2.5.0` - new [Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) [profile](../p/nahsh) diff --git a/package.json b/package.json index 9629243c..7bd26e4e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.5.2", + "version": "2.5.3", "main": "index.js", "files": [ "index.js", From 5d10d76a1575f99cd3684e97b7155db3ed6360ca Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 29 Apr 2018 14:29:29 +0200 Subject: [PATCH 14/33] journey legs: parse cycle --- parse/journey-leg.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 8e7b054a..d85bdf28 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -56,7 +56,15 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => { for (let remark of pt.jny.remL) applyRemark(j, remark) } - if (pt.jny.freq && pt.jny.freq.jnyL) { + const freq = pt.jny.freq || {} + if (freq.minC && freq.maxC) { + // todo: what is freq.numC? + res.cycle = { + min: freq.minC * 60, + max: freq.maxC * 60 + } + } + if (freq.jnyL) { const parseAlternative = (a) => { const t = a.stopL[0].dTimeR || a.stopL[0].dTimeS const when = profile.parseDateTime(profile, j.date, t) @@ -66,8 +74,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => { when: when.toISO() } } - res.alternatives = pt.jny.freq.jnyL - .map(parseAlternative) + res.alternatives = freq.jnyL.map(parseAlternative) } } From 629385599caab9e8335661cbc75700199e963262 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 29 Apr 2018 14:30:05 +0200 Subject: [PATCH 15/33] 2.6.0 --- docs/changelog.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 117cf59c..135c5d70 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## `2.6.0` + +- 5d10d76 journey legs: parse cycle + ## `2.5.3` - d676b84 fix parsing for journey leg alternatives 🐛 diff --git a/package.json b/package.json index 7bd26e4e..f1a314b0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.5.3", + "version": "2.6.0", "main": "index.js", "files": [ "index.js", From 49afc8a75bc7338178c8021af1759d9e519277a7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 30 Apr 2018 12:49:33 +0200 Subject: [PATCH 16/33] journeys: polylines option --- docs/journeys.md | 5 ++++- index.js | 3 ++- p/vbb/example.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/journeys.md b/docs/journeys.md index 152d408d..550a7c15 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -60,7 +60,8 @@ With `opt`, you can override the default options, which look like this: express: true, regional: true }, - tickets: false // return tickets? only available with some profiles + tickets: false, // return tickets? only available with some profiles + polylines: false // return a shape for each leg? } ``` @@ -277,3 +278,5 @@ client.journeys(hbf, heinrichHeineStr) departure of last journey 2017-12-17T19:07:00.000+01:00 departure of first (later) journey 2017-12-17T19:19:00.000+01:00 ``` + +If you pass `polylines: true`, each journey leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. diff --git a/index.js b/index.js index 374c1aa7..e2b178d5 100644 --- a/index.js +++ b/index.js @@ -85,6 +85,7 @@ const createClient = (profile, request = _request) => { accessibility: 'none', // 'none', 'partial' or 'complete' bike: false, // only bike-friendly journeys tickets: false, // return tickets? + polylines: false // return leg shapes? }, opt) if (opt.via) opt.via = profile.formatLocation(profile, opt.via) opt.when = opt.when || new Date() @@ -126,7 +127,7 @@ const createClient = (profile, request = _request) => { getPT: true, // todo: what is this? outFrwd: true, // todo: what is this? getIV: false, // todo: walk & bike as alternatives? - getPolyline: false // todo: shape for displaying on a map? + getPolyline: !!opt.polylines } if (profile.journeysNumF) query.numF = opt.results diff --git a/p/vbb/example.js b/p/vbb/example.js index 079f85c3..0f0831c7 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -6,7 +6,7 @@ const vbbProfile = require('.') const client = createClient(vbbProfile) // Hauptbahnhof to Charlottenburg -client.journeys('900000003201', '900000024101', {results: 1}) +client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.departures('900000013102', {duration: 1}) // client.locations('Alexanderplatz', {results: 2}) // client.location('900000042101') // Spichernstr From 7c6e87afff896ea4c14d6b5c7b0e39fd12b63a12 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 30 Apr 2018 12:49:58 +0200 Subject: [PATCH 17/33] journey legs: return polylines --- index.js | 8 +++++++- p/vbb/index.js | 4 ++-- parse/journey-leg.js | 8 +++++++- parse/journey.js | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index e2b178d5..93c9fdc9 100644 --- a/index.js +++ b/index.js @@ -138,7 +138,13 @@ const createClient = (profile, request = _request) => { }) .then((d) => { if (!Array.isArray(d.outConL)) return [] - const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks) + + let polylines = [] + if (opt.polylines && Array.isArray(d.common.polyL)) { + polylines = d.common.polyL.map(p => p.crdEncYX) + } + const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) + if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB let latestDep = -Infinity diff --git a/p/vbb/index.js b/p/vbb/index.js index f7ff4643..f9be15c1 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -68,8 +68,8 @@ const parseLocation = (profile, l, lines) => { return res } -const createParseJourney = (profile, stations, lines, remarks) => { - const parseJourney = _createParseJourney(profile, stations, lines, remarks) +const createParseJourney = (profile, stations, lines, remarks, polylines) => { + const parseJourney = _createParseJourney(profile, stations, lines, remarks, polylines) const parseJourneyWithTickets = (j) => { const res = parseJourney(j) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index d85bdf28..eab5a3f0 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -4,7 +4,7 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) -const createParseJourneyLeg = (profile, stations, lines, remarks) => { +const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => { // todo: finish parse/remark.js first const applyRemark = (j, rm) => {} @@ -34,6 +34,12 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => { res.arrivalDelay = Math.round((realtime - planned) / 1000) } + if (pt.jny && pt.jny.polyG) { + const p = pt.jny.polyG.polyXL + // todo: there can be >1 polyline + if (p && p.length > 0) res.polyline = polylines[p[0]] || null + } + if (pt.type === 'WALK') { res.mode = 'walking' res.public = true diff --git a/parse/journey.js b/parse/journey.js index db72473c..20a8c0ec 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -2,8 +2,8 @@ const clone = obj => Object.assign({}, obj) -const createParseJourney = (profile, stations, lines, remarks) => { - const parseLeg = profile.parseJourneyLeg(profile, stations, lines, remarks) +const createParseJourney = (profile, stations, lines, remarks, polylines) => { + const parseLeg = profile.parseJourneyLeg(profile, stations, lines, remarks, polylines) // todo: c.sDays // todo: c.dep.dProgType, c.arr.dProgType From 37770654e1fb00d1545236fa996f677c007759a0 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 30 Apr 2018 13:12:05 +0200 Subject: [PATCH 18/33] journeyLeg: polyline option --- docs/journey-leg.md | 5 ++++- index.js | 12 +++++++++--- p/vbb/example.js | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/journey-leg.md b/docs/journey-leg.md index 95de8c14..f54b0e6e 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -25,7 +25,8 @@ With `opt`, you can override the default options, which look like this: ```js { when: new Date(), - passedStations: true // return stations on the way? + passedStations: true, // return stations on the way? + polyline: false // return a shape for the leg? } ``` @@ -116,3 +117,5 @@ The response looked like this: passed: [ /* … */ ] } ``` + +If you pass `polyline: true`, the leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. diff --git a/index.js b/index.js index 93c9fdc9..f6762927 100644 --- a/index.js +++ b/index.js @@ -270,7 +270,8 @@ const createClient = (profile, request = _request) => { throw new Error('lineName must be a non-empty string.') } opt = Object.assign({ - passedStations: true // return stations on the way? + passedStations: true, // return stations on the way? + polyline: false }, opt) opt.when = opt.when || new Date() @@ -280,11 +281,16 @@ const createClient = (profile, request = _request) => { req: { jid: ref, name: lineName, - date: profile.formatDate(profile, opt.when) + date: profile.formatDate(profile, opt.when), + getPolyline: !!opt.polyline } }) .then((d) => { - const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks) + let polylines = [] + if (opt.polyline && Array.isArray(d.common.polyL)) { + polylines = d.common.polyL.map(p => p.crdEncYX) + } + const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks, polylines) const leg = { // pretend the leg is contained in a journey type: 'JNY', diff --git a/p/vbb/example.js b/p/vbb/example.js index 0f0831c7..85d6ea28 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -13,6 +13,10 @@ client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.nearby(52.5137344, 13.4744798, {distance: 60}) // client.radar(52.52411, 13.41002, 52.51942, 13.41709, {results: 10}) +// .then(([journey]) => { +// const leg = journey.legs[0] +// return client.journeyLeg(leg.id, leg.line.name, {polyline: true}) +// }) .then((data) => { console.log(require('util').inspect(data, {depth: null})) }) From 71d087aa23c048f4285f3ac25e33c90052bb9c48 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 30 Apr 2018 13:14:19 +0200 Subject: [PATCH 19/33] refactor polyline parsing, add todo --- index.js | 5 +++-- parse/journey-leg.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index f6762927..ac8f5a8e 100644 --- a/index.js +++ b/index.js @@ -141,7 +141,7 @@ const createClient = (profile, request = _request) => { let polylines = [] if (opt.polylines && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL.map(p => p.crdEncYX) + polylines = d.common.polyL } const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) @@ -279,6 +279,7 @@ const createClient = (profile, request = _request) => { cfg: {polyEnc: 'GPA'}, meth: 'JourneyDetails', req: { + // todo: getTrainComposition jid: ref, name: lineName, date: profile.formatDate(profile, opt.when), @@ -288,7 +289,7 @@ const createClient = (profile, request = _request) => { .then((d) => { let polylines = [] if (opt.polyline && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL.map(p => p.crdEncYX) + polylines = d.common.polyL } const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks, polylines) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index eab5a3f0..65032de2 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -35,9 +35,10 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => } if (pt.jny && pt.jny.polyG) { - const p = pt.jny.polyG.polyXL + let p = pt.jny.polyG.polyXL + p = p && polylines[p[0]] // todo: there can be >1 polyline - if (p && p.length > 0) res.polyline = polylines[p[0]] || null + res.polyline = p && p.crdEncYX || null } if (pt.type === 'WALK') { From 0783d9e68a4b2fe63315e3abd92a44d6df2a0bfc Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 22:42:02 +0200 Subject: [PATCH 20/33] fix Gitter badge --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d5b042ef..70e885b1 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ HAFAS endpoint | wrapper library | docs | example code | source code [![npm version](https://img.shields.io/npm/v/hafas-client.svg)](https://www.npmjs.com/package/hafas-client) [![build status](https://img.shields.io/travis/public-transport/hafas-client.svg)](https://travis-ci.org/public-transport/hafas-client) ![ISC-licensed](https://img.shields.io/github/license/public-transport/hafas-client.svg) -[![chat on gitter](https://badges.gitter.im/derhuerst.svg)](https://gitter.im/derhuerst) +[![chat on gitter](https://badges.gitter.im/public-transport/Lobby.svg)](https://gitter.im/public-transport/Lobby) [![support me on Patreon](https://img.shields.io/badge/support%20me-on%20patreon-fa7664.svg)](https://patreon.com/derhuerst) From c435e253907af5fc244b5847ade5e192b5606423 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 22:52:33 +0200 Subject: [PATCH 21/33] parseJourneyLeg: null as fallback direction :bug: --- parse/journey-leg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 65032de2..fd634a9f 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -48,7 +48,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => // todo: pull `public` value from `profile.products` res.id = pt.jny.jid res.line = lines[parseInt(pt.jny.prodX)] || null - res.direction = profile.parseStationName(pt.jny.dirTxt) + res.direction = profile.parseStationName(pt.jny.dirTxt) || null if (pt.dep.dPlatfS) res.departurePlatform = pt.dep.dPlatfS if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS From 9a0bfc39a45fa69c01dfa0365e34c746c5e43309 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 15 May 2018 19:39:28 +0200 Subject: [PATCH 22/33] radar: polylines option, return polylines --- docs/radar.md | 3 +++ index.js | 9 +++++++-- parse/movement.js | 27 ++++++++++++++++++--------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/docs/radar.md b/docs/radar.md index 281b2f77..8c4fa6a8 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -11,6 +11,7 @@ With `opt`, you can override the default options, which look like this: results: 256, // maximum number of vehicles duration: 30, // compute frames for the next n seconds frames: 3, // nr of frames to compute + polylines: false // return a track shape for each vehicle? } ``` @@ -156,3 +157,5 @@ The response may look like this: } ] }, /* … */ ] ``` + +If you pass `polylines: true`, each result will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. diff --git a/index.js b/index.js index ac8f5a8e..b08a408b 100644 --- a/index.js +++ b/index.js @@ -313,7 +313,8 @@ const createClient = (profile, request = _request) => { results: 256, // maximum number of vehicles duration: 30, // compute frames for the next n seconds frames: 3, // nr of frames to compute - products: null // optionally an object of booleans + products: null, // optionally an object of booleans + polylines: false // return a track shape for each vehicle? }, opt || {}) opt.when = opt.when || new Date() @@ -339,7 +340,11 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.jnyL)) return [] - const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks) + let polylines = [] + if (opt.polylines && Array.isArray(d.common.polyL)) { + polylines = d.common.polyL + } + const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks, polylines) return d.jnyL.map(parse) }) } diff --git a/parse/movement.js b/parse/movement.js index 4a006026..5de96cd9 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,13 +1,12 @@ 'use strict' -const createParseMovement = (profile, locations, lines, remarks) => { +const createParseMovement = (profile, locations, lines, remarks, polylines) => { // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? // todo: what is m.pos? // todo: what is m.ani.dirGeo[n]? maybe the speed? // todo: what is m.ani.proc[n]? wut? - // todo: how does m.ani.poly work? const parseMovement = (m) => { const pStopover = profile.parseStopover(profile, locations, lines, remarks, m.date) @@ -24,13 +23,23 @@ const createParseMovement = (profile, locations, lines, remarks) => { frames: [] } - if (m.ani && Array.isArray(m.ani.mSec)) { - for (let i = 0; i < m.ani.mSec.length; i++) { - res.frames.push({ - origin: locations[m.ani.fLocX[i]] || null, - destination: locations[m.ani.tLocX[i]] || null, - t: m.ani.mSec[i] - }) + if (m.ani) { + if (Array.isArray(m.ani.mSec)) { + for (let i = 0; i < m.ani.mSec.length; i++) { + res.frames.push({ + origin: locations[m.ani.fLocX[i]] || null, + destination: locations[m.ani.tLocX[i]] || null, + t: m.ani.mSec[i] + }) + } + } + + if (m.ani.poly && m.ani.poly.crdEncYX) { + res.polyline = m.ani.poly.crdEncYX + } else if (m.ani.polyG && Array.isArray(m.ani.polyG.polyXL)) { + let p = m.ani.polyG.polyXL[0] + // todo: there can be >1 polyline + res.polyline = polylines[p] && polylines[p].crdEncXY || null } } From 39aac10a178a9b6328a7af1a75722760867a1f9a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 15 May 2018 23:04:13 +0200 Subject: [PATCH 23/33] parseMovement: empty default polylines arr :bug: --- parse/movement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/movement.js b/parse/movement.js index 5de96cd9..5657f053 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,6 +1,6 @@ 'use strict' -const createParseMovement = (profile, locations, lines, remarks, polylines) => { +const createParseMovement = (profile, locations, lines, remarks, polylines = []) => { // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? From 7b5f13524d7bc524428183d0c92da3110b2c1adb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 15 May 2018 23:49:56 +0200 Subject: [PATCH 24/33] 2.7.0 --- docs/changelog.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 135c5d70..3bc5021a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,11 @@ # Changelog +## `2.7.0` + +- `journeys()`: `polylines` option +- `journeyLeg()`: `polyline` option +- `radar()`: `polylines` option + ## `2.6.0` - 5d10d76 journey legs: parse cycle diff --git a/package.json b/package.json index f1a314b0..bb03ae66 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.6.0", + "version": "2.7.0", "main": "index.js", "files": [ "index.js", From aa480e01a2842df7b0686d48a98acf4c2cafba2b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 00:30:27 +0200 Subject: [PATCH 25/33] radar: fix polylines parsing :bug: --- index.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index b08a408b..7ed498ee 100644 --- a/index.js +++ b/index.js @@ -341,7 +341,7 @@ const createClient = (profile, request = _request) => { if (!Array.isArray(d.jnyL)) return [] let polylines = [] - if (opt.polylines && Array.isArray(d.common.polyL)) { + if (opt.polylines && d.common && Array.isArray(d.common.polyL)) { polylines = d.common.polyL } const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks, polylines) diff --git a/package.json b/package.json index bb03ae66..18ce6e92 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.7.0", + "version": "2.7.1", "main": "index.js", "files": [ "index.js", From a97e0d31e7c5009b1f6c4d2695227c07a51a1a54 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 00:42:45 +0200 Subject: [PATCH 26/33] radar: fix polylines parsing :bug:, 2.7.2 --- package.json | 2 +- parse/movement.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 18ce6e92..9df16958 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.7.1", + "version": "2.7.2", "main": "index.js", "files": [ "index.js", diff --git a/parse/movement.js b/parse/movement.js index 5657f053..e2efdf5c 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -39,7 +39,7 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) } else if (m.ani.polyG && Array.isArray(m.ani.polyG.polyXL)) { let p = m.ani.polyG.polyXL[0] // todo: there can be >1 polyline - res.polyline = polylines[p] && polylines[p].crdEncXY || null + res.polyline = polylines[p] && polylines[p].crdEncYX || null } } From 48f2cefb5b2b65a5f84f5c499835c3315ab67322 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 19:30:09 +0200 Subject: [PATCH 27/33] parseMovement: expose journey (leg) id --- parse/movement.js | 1 + 1 file changed, 1 insertion(+) diff --git a/parse/movement.js b/parse/movement.js index e2efdf5c..7999eb52 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -12,6 +12,7 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) const res = { direction: profile.parseStationName(m.dirTxt), + journeyId: m.jid || null, trip: m.jid && +m.jid.split('|')[1] || null, // todo: this seems brittle line: lines[m.prodX] || null, location: m.pos ? { From 0840d69ee9cc0fb9b4bf35237fa93f677991bd7d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:53:33 +0200 Subject: [PATCH 28/33] radar: assert north > south & east > west --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 6d5c082e..ab7f03e6 100644 --- a/index.js +++ b/index.js @@ -316,6 +316,8 @@ const createClient = (profile, request = _request) => { 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.') + if (north <= south) throw new Error('north must be larger than south.') + if (east <= west) throw new Error('east must be larger than west.') opt = Object.assign({ results: 256, // maximum number of vehicles From 0ef03015e06daabf9d1b843ef75d1bd52109713c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 21 May 2018 17:10:42 +0200 Subject: [PATCH 29/33] validate opt.when :boom: --- index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ab7f03e6..c533168f 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,8 @@ const createClient = (profile, request = _request) => { direction: null, // only show departures heading to this station duration: 10 // show departures for the next n minutes }, opt) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const products = profile.formatProductsFilter(opt.products || {}) const dir = opt.direction ? profile.formatStation(opt.direction) : null @@ -96,7 +97,8 @@ const createClient = (profile, request = _request) => { polylines: false // return leg shapes? }, opt) if (opt.via) opt.via = profile.formatLocation(profile, opt.via) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const filters = [ profile.formatProductsFilter(opt.products || {}) @@ -281,7 +283,8 @@ const createClient = (profile, request = _request) => { passedStations: true, // return stations on the way? polyline: false }, opt) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') return request(profile, { cfg: {polyEnc: 'GPA'}, @@ -326,7 +329,8 @@ const createClient = (profile, request = _request) => { products: null, // optionally an object of booleans polylines: false // return a track shape for each vehicle? }, opt || {}) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const durationPerStep = opt.duration / Math.max(opt.frames, 1) * 1000 return request(profile, { From deefeb1f647524595a7747a48558564bc72c2ce3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:07:05 +0200 Subject: [PATCH 30/33] add parsePolyline to default profile --- lib/default-profile.js | 2 ++ package.json | 2 ++ parse/polyline.js | 53 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 parse/polyline.js diff --git a/lib/default-profile.js b/lib/default-profile.js index 340de25f..57d7eb36 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -6,6 +6,7 @@ const parseJourneyLeg = require('../parse/journey-leg') const parseJourney = require('../parse/journey') const parseLine = require('../parse/line') const parseLocation = require('../parse/location') +const parsePolyline = require('../parse/polyline') const parseMovement = require('../parse/movement') const parseNearby = require('../parse/nearby') const parseOperator = require('../parse/operator') @@ -42,6 +43,7 @@ const defaultProfile = { parseLine, parseStationName: id, parseLocation, + parsePolyline, parseMovement, parseNearby, parseOperator, diff --git a/package.json b/package.json index de06f070..d654d719 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,10 @@ "node": ">=6" }, "dependencies": { + "@mapbox/polyline": "^1.0.0", "capture-stack-trace": "^1.0.0", "fetch-ponyfill": "^6.0.0", + "gps-distance": "0.0.4", "lodash": "^4.17.5", "luxon": "^0.5.8", "p-throttle": "^1.1.0", diff --git a/parse/polyline.js b/parse/polyline.js new file mode 100644 index 00000000..5cdcff0c --- /dev/null +++ b/parse/polyline.js @@ -0,0 +1,53 @@ +'use strict' + +const {toGeoJSON} = require('@mapbox/polyline') +const distance = require('gps-distance') + +const createParsePolyline = (locations) => { + // todo: what is p.delta? + // todo: what is p.type? + // todo: what is p.crdEncS? + // todo: what is p.crdEncF? + const parsePolyline = (p) => { + const shape = toGeoJSON(p.crdEncYX) + if (shape.coordinates.length === 0) return null + + const res = shape.coordinates.map(crd => ({ + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: crd + } + })) + + if (Array.isArray(p.ppLocRefL)) { + for (let ref of p.ppLocRefL) { + const p = res[ref.ppIdx] + const loc = locations[ref.locX] + if (p && loc) p.properties = loc + } + + // Often there is one more point right next to each point at a station. + // We filter them here if they are < 5m from each other. + for (let i = 1; i < res.length; i++) { + const p1 = res[i - 1].geometry.coordinates + const p2 = res[i].geometry.coordinates + const d = distance(p1[1], p1[0], p2[1], p2[0]) + if (d >= .005) continue + const l1 = Object.keys(res[i - 1].properties).length + const l2 = Object.keys(res[i].properties).length + if (l1 === 0 && l2 > 0) res.splice(i - 1, 1) + else if (l2 === 0 && l1 > 0) res.splice(i, 1) + } + } + + return { + type: 'FeatureCollection', + features: res + } + } + return parsePolyline +} + +module.exports = createParsePolyline From 431574b7569754985e17dcaf66ccda67cb9408e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:07:43 +0200 Subject: [PATCH 31/33] parseJourneyLeg, parseMovement: use parsePolyline :boom:, refactor --- index.js | 15 +++------------ parse/journey-leg.js | 5 +++-- parse/movement.js | 13 ++++++++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index c533168f..d2410c70 100644 --- a/index.js +++ b/index.js @@ -149,10 +149,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.outConL)) return [] - let polylines = [] - if (opt.polylines && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB @@ -298,10 +295,7 @@ const createClient = (profile, request = _request) => { } }) .then((d) => { - let polylines = [] - if (opt.polyline && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks, polylines) const leg = { // pretend the leg is contained in a journey @@ -354,10 +348,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.jnyL)) return [] - let polylines = [] - if (opt.polylines && d.common && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks, polylines) return d.jnyL.map(parse) }) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index fd634a9f..bbc3f9da 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -36,9 +36,10 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => if (pt.jny && pt.jny.polyG) { let p = pt.jny.polyG.polyXL - p = p && polylines[p[0]] + p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - res.polyline = p && p.crdEncYX || null + const parse = profile.parsePolyline(stations) + res.polyline = p && parse(p) || null } if (pt.type === 'WALK') { diff --git a/parse/movement.js b/parse/movement.js index 7999eb52..8a3ab401 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -35,12 +35,15 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) } } - if (m.ani.poly && m.ani.poly.crdEncYX) { - res.polyline = m.ani.poly.crdEncYX - } else if (m.ani.polyG && Array.isArray(m.ani.polyG.polyXL)) { - let p = m.ani.polyG.polyXL[0] + if (m.ani.poly) { + const parse = profile.parsePolyline(locations) + res.polyline = parse(m.ani.poly) + } else if (m.ani.polyG) { + let p = m.ani.polyG.polyXL + p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - res.polyline = polylines[p] && polylines[p].crdEncYX || null + const parse = profile.parsePolyline(locations) + res.polyline = p && parse(p) || null } } From 8c896fe8c1b88905e565852afed4c2c9d5cfc1f9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:27:12 +0200 Subject: [PATCH 32/33] docs for new polyline format :memo: --- docs/journey-leg.md | 64 ++++++++++++++++++++++++++++++++++++++++++++- docs/journeys.md | 2 +- docs/radar.md | 2 +- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/docs/journey-leg.md b/docs/journey-leg.md index f54b0e6e..e21293c7 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -118,4 +118,66 @@ The response looked like this: } ``` -If you pass `polyline: true`, the leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +### `polyline` option + +If you pass `polyline: true`, the leg will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata. + +We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb): + +```js +{ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + type: 'station', + id: '900000070301', + name: 'U Alt-Mariendorf', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.3875, 52.43993] // longitude, latitude + } + }, + /* … */ + { + type: 'Feature', + properties: { + type: 'station', + id: '900000017101', + name: 'U Mehringdamm', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.38892, 52.49448] // longitude, latitude + } + }, + /* … */ + { + // intermediate point, without associated station + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [13.28599, 52.58742] // longitude, latitude + } + }, + { + type: 'Feature', + properties: { + type: 'station', + id: '900000089301', + name: 'U Alt-Tegel', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.28406, 52.58915] // longitude, latitude + } + } + ] +} +``` diff --git a/docs/journeys.md b/docs/journeys.md index 1b6d0051..016f052c 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -262,4 +262,4 @@ departure of last journey 2017-12-17T19:07:00.000+01:00 departure of first (later) journey 2017-12-17T19:19:00.000+01:00 ``` -If you pass `polylines: true`, each journey leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `journeyLeg()` docs](journey-leg.md#polyline-option) for details. diff --git a/docs/radar.md b/docs/radar.md index ab00b767..e42155c0 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -163,4 +163,4 @@ The response may look like this: }, /* … */ ] ``` -If you pass `polylines: true`, each result will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +If you pass `polylines: true`, each journey leg will have a `polyline` field, as documented in [the corresponding section in the `journeyLeg()` docs](journey-leg.md#polyline-option), with the exception that station info is missing. From 03b9ab9a839fa15332b8dd2bd2b4ba47eebddf9c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 22:28:59 +0200 Subject: [PATCH 33/33] expect parsePolyline in lib/validate-profile --- lib/validate-profile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 21c86829..88e3d0c1 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -16,6 +16,7 @@ const types = { parseLine: 'function', parseStationName: 'function', parseLocation: 'function', + parsePolyline: 'function', parseMovement: 'function', parseNearby: 'function', parseOperator: 'function',