db-vendo-client/parse/journey-leg.js
2024-03-12 12:26:46 +01:00

233 lines
7.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {findRemarks} from './find-remarks.js';
const clone = obj => Object.assign({}, obj);
const addRemark = (stopoverOrLeg, remark) => {
if (!Array.isArray(stopoverOrLeg.remarks)) {
stopoverOrLeg.remarks = [];
}
stopoverOrLeg.remarks.push(remark);
};
const applyRemarks = (leg, refs) => {
for (let [remark, ref] of findRemarks(refs)) {
const {fromLocation, toLocation} = ref;
let wholeLeg = true, fromI = 0, toI = 0;
if (Array.isArray(leg.stopovers)) {
toI = leg.stopovers.length - 1;
// this fails if `s.stop` is a new object (not reference-equal)
// todo: do this index- or ID-based
if (fromLocation) {
fromI = leg.stopovers.findIndex(s => s.stop === fromLocation);
if (fromI < 0) {
continue;
}
}
if (toLocation) {
toI = leg.stopovers.findIndex(s => s.stop === toLocation);
if (toI < 0) {
continue;
}
}
wholeLeg = fromI === 0 && toI === leg.stopovers.length - 1;
}
if (wholeLeg) {
addRemark(leg, remark);
} else {
for (let i = fromI; i <= toI; i++) {
const stopover = leg.stopovers[i];
if (stopover) {
addRemark(stopover, remark);
}
}
}
// todo: `ref.tagL`
}
};
// todo: pt.status, pt.isPartCncl
// todo: pt.chRatingRT, pt.chgDurR, pt.minChg
// todo: pt.jny.dirFlg https://github.com/alexander-albers/tripkit/blob/07047c6ddef24339ebd49a86a78158bca8047421/Sources/TripKit/Provider/AbstractHafasClientInterfaceProvider.swift#L347-L353 & https://github.com/alexander-albers/tripkit/commit/07047c6ddef24339ebd49a86a78158bca8047421#commitcomment-68471656
// todo: what is pt.recState?
// todo: what is `sty: 'UNDEF'`?
// todo: pt.prodL
// todo: pt.parJnyL (list of coupled trains)
// todo: pt.planrtTS
// todo: what is pt.jny.lPassSt?
const parseJourneyLeg = (ctx, pt, date) => { // pt = raw leg
const {profile, opt} = ctx;
const res = {
origin: clone(pt.dep.location) || null,
destination: clone(pt.arr.location),
};
// HAFAS seems to have a bug where a journey's first leg has a `dTZOffset` of `0`.
// https://github.com/public-transport/hafas-client/issues/237
if (pt.type === 'WALK' && pt.dep.dTZOffset != pt.arr.aTZOffset) {
if (pt.dep.dTZOffset == 0) {
pt.dep.dTZOffset = pt.arr.aTZOffset;
}
if (pt.arr.aTZOffset == 0) {
pt.arr.aTZOffset = pt.dep.dTZOffset;
}
}
const dep = profile.parseWhen(ctx, date, pt.dep.dTimeS, pt.dep.dTimeR, pt.dep.dTZOffset, pt.dep.dCncl);
res.departure = dep.when;
res.plannedDeparture = dep.plannedWhen;
// todo: pt.dep.dProgType
res.departureDelay = dep.delay;
if (dep.prognosedWhen) {
res.prognosedDeparture = dep.prognosedWhen;
}
const arr = profile.parseWhen(ctx, date, pt.arr.aTimeS, pt.arr.aTimeR, pt.arr.aTZOffset, pt.arr.aCncl);
res.arrival = arr.when;
res.plannedArrival = arr.plannedWhen;
// todo: pt.arr.aProgType
res.arrivalDelay = arr.delay;
if (arr.prognosedWhen) {
res.prognosedArrival = arr.prognosedWhen;
}
if (pt.jny && 'isRchbl' in pt.jny) {
res.reachable = Boolean(pt.jny.isRchbl);
}
if (pt.jny && pt.jny.polyline) {
res.polyline = pt.jny.polyline || null;
} else if (pt.jny && pt.jny.poly) {
res.polyline = profile.parsePolyline(ctx, pt.jny.poly);
}
if (pt.type === 'WALK' || pt.type === 'TRSF' || pt.type === 'DEVI' || pt.type === 'CHKI') {
res.public = true;
res.walking = true;
res.distance = pt.gis && pt.gis.dist || null;
if (pt.type === 'TRSF') {
res.transfer = true;
}
if (pt.type === 'DEVI') {
// > DEVI Legs are Deviations.
// > It happens if you ask for a route to a specific stopPlace but HAFAS returns one to another stopPlace. Happens mainly in the night when there is no route to you destination for the forseeable future.
// > For Instance you want to go from Berlin Hbf to München Hbf. One of the only routes to München is to München Ost with a NJ at your time.
// > HAFAS returns that NJ and the last leg is a DEVI that just says "you wanted München HBF, this route ends in München Ost"
// > Bahn.de for instance ignores this completly, the DEVI leg also has no real information.
// https://github.com/public-transport/hafas-client/issues/301#issuecomment-1840820895
// todo: pt.resState, pt.resRecommendation
res.transfer = true;
}
if (pt.type === 'CHKI') {
res.checkin = true;
}
// https://gist.github.com/derhuerst/426d4b95aeae701843b1e9c23105b8d4#file-tripsearch-2018-12-05-http-L4207-L4229
if (opt.remarks && pt.gis && Array.isArray(pt.gis.msgL)) {
applyRemarks(res, pt.gis.msgL);
}
} else if (pt.type === 'JNY') {
// todo: pull `public` value from `profile.products`
res.tripId = pt.jny.jid;
res.line = pt.jny.line || null;
// todo [breaking]: don't call parseStationName() here, add parseDirection() hook
// todo: support pt.jny.dirL[]
res.direction = pt.jny.dirTxt && profile.parseStationName(ctx, pt.jny.dirTxt) || null;
if (pt.jny.pos) {
res.currentLocation = {
type: 'location',
latitude: pt.jny.pos.y / 1000000,
longitude: pt.jny.pos.x / 1000000,
};
}
const arrPl = profile.parsePlatform(ctx, pt.arr.aPlatfS || (pt.arr.aPltfS !== undefined
? pt.arr.aPltfS.txt
: null), pt.arr.aPlatfR || (pt.arr.aPltfR !== undefined
? pt.arr.aPltfR.txt
: null), pt.arr.aCncl);
res.arrivalPlatform = arrPl.platform;
res.plannedArrivalPlatform = arrPl.plannedPlatform;
if (arrPl.prognosedPlatform) {
res.prognosedArrivalPlatform = arrPl.prognosedPlatform;
}
res.arrivalPrognosisType = profile.parsePrognosisType(ctx, pt.arr.aProgType) || null;
const depPl = profile.parsePlatform(ctx, pt.dep.dPlatfS || (pt.dep.dPltfS !== undefined
? pt.dep.dPltfS.txt
: null), pt.dep.dPlatfR || (pt.dep.dPltfR !== undefined
? pt.dep.dPltfR.txt
: null), pt.dep.dCncl);
res.departurePlatform = depPl.platform;
res.plannedDeparturePlatform = depPl.plannedPlatform;
if (depPl.prognosedPlatform) {
res.prognosedDeparturePlatform = depPl.prognosedPlatform;
}
res.departurePrognosisType = profile.parsePrognosisType(ctx, pt.dep.dProgType) || null;
if (opt.stopovers && pt.jny.stopL) {
const stopL = pt.jny.stopL;
res.stopovers = stopL.map(s => profile.parseStopover(ctx, s, date));
if (opt.remarks && Array.isArray(pt.jny.msgL)) {
applyRemarks(res, pt.jny.msgL);
// todo: parse & use `code: EXTERNAL_ID` remarks?
}
// filter stations the train passes without stopping, as this doesn't comply with fptf (yet)
res.stopovers = res.stopovers.filter((x) => !x.passBy);
} else if (opt.remarks && Array.isArray(pt.jny.msgL)) {
applyRemarks(res, pt.jny.msgL);
}
const freq = pt.jny.freq || {};
// todo: expose `res.cycle` even if only one field exists (breaking)
// todo [breaking]: rename to something more intuitive, e.g. res.headways.{min,max,nrOfTrips}
if (freq.minC && freq.maxC) {
res.cycle = {
min: freq.minC * 60,
max: freq.maxC * 60,
};
// nr of connections in this frequency, from now on
if (freq.numC) {
res.cycle.nr = freq.numC;
}
}
if (freq.jnyL) {
const parseAlternative = (a) => {
// todo: parse this just like a `leg` (breaking)
// todo: parse `a.stopL`, `a.ctxRecon`, `a.msgL`
const st0 = Array.isArray(a.stopL) && a.stopL[0] || {};
const when = st0
? profile.parseWhen(ctx, date, st0.dTimeS, st0.dTimeR, st0.dTZOffset, st0.dCncl)
: null;
return {
tripId: a.jid,
line: a.line || null,
direction: a.dirTxt || null,
...when,
};
};
res.alternatives = freq.jnyL.map(parseAlternative);
}
}
if (pt.arr.aCncl || pt.dep.dCncl) {
res.cancelled = true;
Object.defineProperty(res, 'canceled', {value: true});
}
return res;
};
export {
parseJourneyLeg,
};