Skip to content

Add netexMode and netexSubmode to GTFS GraphQL Trip type #6676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: dev-2.x
Choose a base branch
from

Conversation

tkalvas
Copy link
Contributor

@tkalvas tkalvas commented May 28, 2025

Summary

GTFS and NeTEx have trip modes and submodes in different places. This PR makes it possible to read modes and submodes, using the GTFS graphql interface, from trips, not routes. If they are not defined in trips, they will be read from routes, but one defined in the trip takes precedence. The format of the submode is the same as in NeTEx (i.e. "railReplacementBus", not 714).

Digitransit (Finland) will move into a transitory period when some things will be in NeTEx and some things in GTFS, possibly for multiple years. Something like this is required because we need a mechanism to query data from both sources, GTFS and NeTEx.

Unit tests

None yet. Let's discuss if this approach is ok first.

Changelog

Yes.

Bumping the serialization version id

No.

@tkalvas tkalvas requested a review from a team as a code owner May 28, 2025 09:40
Copy link

codecov bot commented May 28, 2025

Codecov Report

Attention: Patch coverage is 53.33333% with 14 lines in your changes missing coverage. Please review.

Project coverage is 71.39%. Comparing base (062eeb2) to head (29be6f3).
Report is 160 commits behind head on dev-2.x.

Files with missing lines Patch % Lines
...ntripplanner/apis/gtfs/generated/GraphQLTypes.java 46.15% 7 Missing ⚠️
...entripplanner/apis/gtfs/datafetchers/TripImpl.java 33.33% 6 Missing ⚠️
...ner/apis/gtfs/mapping/TransmodelSubmodeMapper.java 87.50% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             dev-2.x    #6676      +/-   ##
=============================================
+ Coverage      71.31%   71.39%   +0.08%     
- Complexity     18518    18575      +57     
=============================================
  Files           2031     2036       +5     
  Lines          76462    76544      +82     
  Branches        7820     7813       -7     
=============================================
+ Hits           54527    54647     +120     
+ Misses         19165    19136      -29     
+ Partials        2770     2761       -9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@@ -2496,6 +2496,10 @@ type Trip implements Node {
gtfsId: String!
"Global object ID provided by Relay. This value can be used to refetch this object using **node** query."
id: ID!
"Mode of the trip as a netex mode (string), gtfs modes of routes are mapped into them if necessary."
netexMode: String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this isn't the NeTEx mode but applies to GTFS trips as well, doesn't it?

I think you can return the TransitMode instance rather than a String.

(I don't want to preempt the discussion.)

@optionsome optionsome changed the title add netexMode and netexSubmode to gtfs graphql trip interface Add netexMode and netexSubmode to gtfs graphql trip type Jun 3, 2025
@@ -2496,6 +2496,10 @@ type Trip implements Node {
gtfsId: String!
"Global object ID provided by Relay. This value can be used to refetch this object using **node** query."
id: ID!
"Mode of the trip as a netex mode (string), gtfs modes of routes are mapped into them if necessary."
netexMode: String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
netexMode: String
mode: TransitMode

@leonardehrenfried leonardehrenfried changed the title Add netexMode and netexSubmode to gtfs graphql trip type Add netexMode and netexSubmode to GTFS GraphQL Trip type Jun 3, 2025
@@ -199,14 +200,10 @@ public static class GraphQLBicycleParkingPreferencesInput {
public GraphQLBicycleParkingPreferencesInput(Map<String, Object> args) {
if (args != null) {
if (args.get("filters") != null) {
this.filters = ((List<Map<String, Object>>) args.get("filters")).stream()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to run yarn install again before generating this code. The library version has changed: #6647

@miklcct
Copy link
Contributor

miklcct commented Jun 3, 2025

Why do you need to add netexSubmode into the GTFS API? I think that the GTFS API should not contain any Transmodel, likewise Transmodel API should not contain any GTFS.

The modes should be mapped in our internal model, e.g. we should treat 714 from a GTFS data source to be the same as a RAIL_REPLACEMENT_BUS from a NeTEx data source, so no matter if the source data is from GTFS or from NeTEx, 714 will be returned in the GTFS API and RAIL_REPLACEMENT_BUS in the Transmodel API.

In concrete terms, I suggest that we add mode: TransitMode and gtfsType: Int in the GTFS API Trip type. The latter will be null if a NeTEx submode does not have an equivalent in the Google Extended GTFS route types.

@tkalvas
Copy link
Contributor Author

tkalvas commented Jun 5, 2025

In concrete terms, I suggest that we add mode: TransitMode and gtfsType: Int in the GTFS API Trip type. The latter will be null if a NeTEx submode does not have an equivalent in the Google Extended GTFS route types.

This is a possibility. But exactly because of the possible lack of equivalence I think that both versions (gtfs and netex) should be exposed.

The other possibility, and possibly a nicer one, is to make the actual mapper configurable, so that when there are equivalence problems, the problem is not an OTP problem but a HSL problem which we can solve by editing a file called FinlandTransmodelSubmodeMapper or something. And then if there is an "unmappable" netex submode, I'll just invent a random very high number and use it as the corresponding type. Ugly, but limited to my installation only.

@miklcct
Copy link
Contributor

miklcct commented Jun 5, 2025

The other possibility, and possibly a nicer one, is to make the actual mapper configurable, so that when there are equivalence problems, the problem is not an OTP problem but a HSL problem which we can solve by editing a file called FinlandTransmodelSubmodeMapper or something. And then if there is an "unmappable" netex submode, I'll just invent a random very high number and use it as the corresponding type. Ugly, but limited to my installation only.

I think this is the way to go, with configurable mappings in the configuration JSON (which includes sensible defaults e.g. 714 = replacement bus).

If you have clients who want to know about the NeTEx they should use the NeTEx API.

@tkalvas
Copy link
Contributor Author

tkalvas commented Jun 5, 2025

If you have clients who want to know about the NeTEx they should use the NeTEx API.

I think, and I think also Thomas thinks, that a client should be able to not care whether the original data is gtfs or netex.

The business situation here is that we will be getting some data as netex and some as gtfs for several years, but it makes life for the frontend developer unnecessarily hard if they have to care.

@leonardehrenfried
Copy link
Member

I like what we discussed in the last meeting: keep submode internal and introduce API properties for what the submode is currently used for. In your case you want to know if something is a rail replacement bus.

@miklcct
Copy link
Contributor

miklcct commented Jun 5, 2025

If you have clients who want to know about the NeTEx they should use the NeTEx API.

I think, and I think also Thomas thinks, that a client should be able to not care whether the original data is gtfs or netex.

The business situation here is that we will be getting some data as netex and some as gtfs for several years, but it makes life for the frontend developer unnecessarily hard if they have to care.

Yes, I think that a client should be able to not care whether the original data is gtfs or netex, but your proposal does the exact opposite thing. (it forces user to use a certain API and a certain field depending on the source data, as gtfsType from the GTFS API is currently null for NeTEx data)

If they want to know NeTEx they should use the NeTEx API, which can be used across both data sources with the correct GTFS types mapped to the NeTEx types (e.g. GTFS 714 will be returned as Replacement Bus in the NeTEx API), while if they are using GTFS they should start seeing 714 for all kinds of replacement buses even if the replacement bus is from NeTEx data.

@tkalvas
Copy link
Contributor Author

tkalvas commented Jun 5, 2025

Oh, the version of this PR right now is definitely not going forward. I'm just floating ideas because there are multiple competing views on how this should be done.

Leonard's suggestion would mean that there is something like trip.replacementMode = 'BUS' which would be populated by trip.route.type = 714 || trip.submode = railReplacementBus.

Your suggestion would mean there is trip.type = 714 which is populated by trip.route.type = 714 || trip.submode = railReplacementBus.

So not that different in implementation right now, but they indicate different futures of the interface. Leonard's suggestion was born in Tuesday's dev meeting because everyone agrees that both the gtfs type and netex submode collapse several different dimensions into one, and that makes it almost completely useless.

@leonardehrenfried
Copy link
Member

leonardehrenfried commented Jun 6, 2025

We discussed this in yesterday's meeting and agreed that we will not expose the submode itself but the properties that the submode is supposed to be communicating. In this case we want to know if it's a replacement bus and @tkalvas will propose an API for it. There was talk about a generic container object holding these properties but it's hard to predict what we will need in the future, so I'm not sure how well we can plan for it.

How we extract this property from the input feeds may or may not be deployment-specific. If necessary it can be made configurable later.

Is this what we agreed?

@optionsome optionsome marked this pull request as draft June 10, 2025 08:11
@t2gran
Copy link
Member

t2gran commented Jun 10, 2025

Design Note

NeTEx submodes / GTFS extended route types are not clearly defined - and poorly designed. The sub-modes is just a label to group a set of properties(geo. service area [local, regional, national, internatonal], Wifi, bike raks, food options, seating and sleeping facilities, ticket options, express and so on). What properties a submode mappes to is usually deployment specific and may also be feed specific. To deal with this my suggested strategy is to create each property on the OTP Internal model - these can be exposed in filters and properties on the APIs.

To map into the OTP Internal model we should create a mapping matrix. If we can agree on the matrix we can keep it in OTP, if not we need to make it configurable, but I suggest that we just use a simple CSV file for it. Then overriding the default in config would be easy at any point in time(now or later).

Mapping table

We only add columns now that are requested, other columns can be added later. This is just a draft, example details are included to make it easier to understand. The target properties can be basic types like boolean, numbers, strings, scalars (duration, time), enums, or expressions.

Input Feed Type  Input Label OTP GTFS ext route type OTP Netex Submode  Geo Service Area Express Replacement Service
GTFS 714 714 railReplacementBus NA NA RAIL
NeTEx railReplacementBus 714 railReplacementBus NA NA RAIL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants