public-transport / hafas-client

JavaScript client for HAFAS public transport APIs.
ISC License
278 stars 54 forks source link

Add origin/destination on departures/arrivals #260

Closed bddq closed 2 years ago

bddq commented 2 years ago

I renamed direction/provenance properties to headsign and added a more complete info on origin/destination property if available.

This will help to close issue #259.

derhuerst commented 2 years ago

About the headsign renaming: Unfortunately, the dirTxt is not the headsign (as in the text used to identify the trip/route, usually displayed on the vehicle), because for regular scheduled (non-shuttle, non-replacement-service, etc.) trips, it usually contains the direction, but with arrivals, it contains the origin. See https://github.com/public-transport/hafas-client/issues/179#issuecomment-641178783 for details.

bddq commented 2 years ago

@derhuerst I reverted headsign breaking change.

derhuerst commented 2 years ago

If you have time for it, you can adapt >=1 of the following parseDepartures/departures() tests (whose fixtures contain tLocX/fLocX) to check if destination is being parsed:

Otherwise I can do it as well.

derhuerst commented 2 years ago

Looking at a trip through-serviced with another trip of a different route/line, we can see that locL[tLocX] represents the last stop of the through-serviced trip:

./tools/debug-cli/cli.js vbb departures 900000009102 | jq '.[] | [.tripId, .line.name, .direction, .destination]'
[
    "1|68827|2|86|19022022",
    "120",
    "S+U Wittenau -> Bus 220",
    {
        "type": "stop",
        "id": "900000092202",
        "name": "Hainbuchenstr.",
        "location": {
            "type": "location",
            "id": "900092202",
            "latitude": 52.634251,
            "longitude": 13.27482
        },
        "products": {
            "suburban": false,
            "subway": false,
            "tram": false,
            "bus": true,
            "ferry": false,
            "express": false,
            "regional": false
        }
    }
]

When querying such trips, HAFAS returns includes the stopovers of the respective through-serviced 2nd trip in .stopL[] and adds a remark at the stopover where the route/line changes:

./tools/debug-cli/cli.js vbb trip '1|68827|2|86|19022022' '120' | jq
{
    "id": "1|68827|2|86|19022022",
    "reachable": true,
    "line": {
        "type": "line",
        "id": "120",
        "fahrtNr": "2876",
        "name": "120",
        "public": true,
        "adminCode": "BVB",
        "productName": "Bus",
        "mode": "bus",
        "product": "bus",
        "operator": {
            "type": "operator",
            "id": "berliner-verkehrsbetriebe",
            "name": "Berliner Verkehrsbetriebe"
        },
        "symbol": null,
        "nr": 120,
        "metro": false,
        "express": false,
        "night": false
    },
    "direction": "S+U Wittenau -> Bus 220",
    "currentLocation": {
        "type": "location",
        "latitude": 52.537815,
        "longitude": 13.372821
    },

    "origin": {
        "type": "stop",
        "id": "900000002255",
        "name": "Seydlitzstr.",
        "location": {
            "type": "location",
            "id": "900002255",
            "latitude": 52.527037,
            "longitude": 13.364155
        }
    },
    "departure": "2022-02-19T22:15:00+01:00",
    "plannedDeparture": "2022-02-19T22:15:00+01:00",
    "departureDelay": 0,

    "destination": {
        "type": "stop",
        "id": "900000092202",
        "name": "Hainbuchenstr.",
        "location": {
            "type": "location",
            "id": "900092202",
            "latitude": 52.634251,
            "longitude": 13.27482
        }
    },
    "arrival": "2022-02-19T23:07:00+01:00",
    "plannedArrival": "2022-02-19T23:06:00+01:00",
    "arrivalDelay": 60,

    "stopovers": [
        {
            "stop": {
                "type": "stop",
                "id": "900000002255",
                "name": "Seydlitzstr.",
                "location": {
                    "type": "location",
                    "id": "900002255",
                    "latitude": 52.527037,
                    "longitude": 13.364155
                }
            },
            "arrival": null,
            "plannedArrival": null,
            "arrivalDelay": null,
            "departure": "2022-02-19T22:15:00+01:00",
            "plannedDeparture": "2022-02-19T22:15:00+01:00",
            "departureDelay": 0,
            "remarks": [
                {
                    "type": "hint",
                    "code": "text.journeystop.product.or.direction.changes.stop.message",
                    "text": "Verkehrt ab hier als 120 in Richtung S+U Wittenau -> Bus 220"
                }
            ]
        },
        {
            "stop": {
                "type": "stop",
                "id": "900000001204",
                "name": "Lehrter Str./Invalidenstr.",
                "location": {
                    "type": "location",
                    "id": "900001204",
                    "latitude": 52.52576,
                    "longitude": 13.365225
                },
                "products": {
                    "suburban": false,
                    "subway": false,
                    "tram": true,
                    "bus": true,
                    "ferry": false,
                    "express": false,
                    "regional": false
                }
            },
            "arrival": "2022-02-19T22:16:00+01:00",
            "plannedArrival": "2022-02-19T22:16:00+01:00",
            "arrivalDelay": 0,
            "departure": "2022-02-19T22:16:00+01:00",
            "plannedDeparture": "2022-02-19T22:16:00+01:00",
            "departureDelay": 0
        },
        // …
        {
            "stop": {
                "type": "stop",
                "id": "900000096454",
                "name": "Techowpromenade",
                "location": {
                    "type": "location",
                    "id": "900096454",
                    "latitude": 52.593071,
                    "longitude": 13.333834
                }
            },
            "arrival": "2022-02-19T22:45:00+01:00",
            "plannedArrival": "2022-02-19T22:45:00+01:00",
            "arrivalDelay": 0,
            "departure": "2022-02-19T22:45:00+01:00",
            "plannedDeparture": "2022-02-19T22:45:00+01:00",
            "departureDelay": 0
        },
        {
            "stop": {
                "type": "stop",
                "id": "900000096407",
                "name": "U Wittenau [Bus]",
                "location": {
                    "type": "location",
                    "id": "900096407",
                    "latitude": 52.595507,
                    "longitude": 13.333304
                },
                "products": {
                    "suburban": true,
                    "subway": true,
                    "tram": false,
                    "bus": true,
                    "ferry": false,
                    "express": false,
                    "regional": false
                }
            },
            "arrival": "2022-02-19T22:46:00+01:00",
            "plannedArrival": "2022-02-19T22:46:00+01:00",
            "arrivalDelay": 0,
            "departure": "2022-02-19T22:47:00+01:00",
            "plannedDeparture": "2022-02-19T22:46:00+01:00",
            "departureDelay": 60,
            "remarks": [
                {
                    "type": "hint",
                    "code": "text.journeystop.product.or.direction.changes.stop.message",
                    "text": "Verkehrt ab hier als 220 in Richtung Frohnau, Hainbuchenstr."
                }
            ]
        },
        {
            "stop": {
                "type": "stop",
                "id": "900000096193",
                "name": "Göschenplatz/S Wittenau",
                "location": {
                    "type": "location",
                    "id": "900096193",
                    "latitude": 52.597404,
                    "longitude": 13.332558
                },
                "products": {
                    "suburban": true,
                    "subway": false,
                    "tram": false,
                    "bus": true,
                    "ferry": false,
                    "express": false,
                    "regional": false
                }
            },
            "arrival": "2022-02-19T22:48:00+01:00",
            "plannedArrival": "2022-02-19T22:47:00+01:00",
            "arrivalDelay": 60,
            "departure": "2022-02-19T22:48:00+01:00",
            "plannedDeparture": "2022-02-19T22:47:00+01:00",
            "departureDelay": 60
        },
        // …
        {
            "stop": {
                "type": "stop",
                "id": "900000092202",
                "name": "Hainbuchenstr.",
                "location": {
                    "type": "location",
                    "id": "900092202",
                    "latitude": 52.634251,
                    "longitude": 13.27482
                }
            },
            "arrival": "2022-02-19T23:07:00+01:00",
            "plannedArrival": "2022-02-19T23:06:00+01:00",
            "arrivalDelay": 60,
            "departure": null,
            "plannedDeparture": null,
            "departureDelay": null
        }
    ],
    "remarks": [
        {
            "type": "hint",
            "code": "bf",
            "text": "barrierefrei"
        },
        {
            "type": "hint",
            "code": "text.journeystop.product.or.direction.changes.journey.message",
            "text": "Verkehrt ab U Wittenau (Berlin) [Bus] als 220 in Richtung Frohnau, Hainbuchenstr."
        }
    ],
}

Unfortunately, with departures()/arrivals(), there is no such remark allowing us to detect the through-serviced trip.

It's now up to use to decide: Do we want to expose this information, which is technically correct from an operational perspective but definitely very confusing for consuming applications and users who are not familiar with this detail of public transport networks?

bddq commented 2 years ago

I understand your point of view about through-serviced trips.

I think your example illustrate that client apps should present remarks on a trip to help user to understand what changes during the journey.

As seen on the DB Navigator app, I will take Bus 120 from Seydlitzstr. to Hainbuchenstr. with a user info that bus number will change from Wittenau to destination.

From my point of view this is not a problem to display through-serviced trips with the full trip stop references for origin/destination and relying on remarks to display the right info to the user.

derhuerst commented 2 years ago

In an ideal world, I think it should be the other way around:

But: If we decide to a) move the through-serviced stopovers of a trip returned by trip() into a separate field, and b) keep the through-serviced destination in the corresponding departures() result, I'd say that would be really inconsistent.

I therefore think that we have the choice: Does hafas-client try to be more developer-/user-friendly by abstracting away some of the design & technical limitations of HAFAS, at the cost of inconsistent abstractions? Or should it represent whatever it has fetched in a raw & "authentic" way?

Given that the former hasn't worked out very well for hafas-client so far (provenance/direction are not 100% semantically precise, .line fits neither FPTF nor HAFAS semantically, etc.), I have decided to merge your PR and don't split through-serviced stopovers from a trip.

derhuerst commented 2 years ago

I have squash-merged your PR and amended a few fixes in the tests.

derhuerst commented 2 years ago

Do you want to mentioned as a contributor in package.json? If yes, with which signature? bddq?

bddq commented 2 years ago

Do you want to mentioned as a contributor in package.json? If yes, with which signature? bddq?

Thanks. You can put Benoit Deldicque (https://github.com/bddq).

derhuerst commented 2 years ago

Published as hafas-client@5.23.0. 🎉