Skip to content
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

FIX: 158 - Ensure GTFS and RAPTOR router have the same stop set #159

Merged
merged 18 commits into from
Jan 14, 2025

Conversation

munterfi
Copy link
Member

@munterfi munterfi commented Dec 9, 2024

If a stop occurs in the GTFS schedule, it should be included in RAPTOR, even if no departures are scheduled from that stop. While adding a large number of such stops could negatively impact performance during the generation of transfers at startup, the effect on routing would be minimal since no route scanning occurs from these stops.

When transfers are generated, it could be that connections from or to stops with no trips are available via a footpath / transfer as first or last leg. Currently we are not generating (between stop) transfers when no trip is active on a stop.

Since this issue originates in the GTFS input, we could issue a warning when adding stops with no departures. Ultimately, however, it is the responsibility of the GTFS producer to address this. Our public transit service should handle such cases gracefully but should not attempt to fix the underlying data issue.

This PR:

  • Adds all stops to RAPTOR, even if they have no departures / stopping trips or transfers.
  • Adjust and refactor GtfsToRaptorConverterIT (make tests passing, asserts are checking actual transfer ids or keys, differentiate between same stop transfers and between stop transfers)

@clukas1: This is an alternative to PR #160 to address the issue #158, which I would prefer due to the consistency between the data in the schedule and the router.

- Add all stops to RAPTOR, even if they have no departures / stopping trips.
- When transfers are generated, it could be that connections from or to stops with no trips are available via a footpath / transfer as first or last leg.
@munterfi munterfi requested a review from clukas1 December 9, 2024 16:29
@munterfi munterfi linked an issue Dec 9, 2024 that may be closed by this pull request
@munterfi munterfi marked this pull request as draft December 9, 2024 16:30
 - Test passes when all stops are added to raptor, even if they have no departures.
 - The asserts are checking actual transfer ids or keys, which makes it easier to follow the test cases.
 - Clearly differentiate between same stop transfers and between stop transfers.
 - Suppress unchecked cast compiler warnings in test cases.
@munterfi munterfi self-assigned this Jan 4, 2025
@munterfi munterfi marked this pull request as ready for review January 4, 2025 01:47
@munterfi munterfi added bug Something isn't working enhancement New feature or request labels Jan 4, 2025
- The RAPTOR should not contain all stops, due to the parent stops, which often have not got any departures but would increase the stop array size significantly.
Now only stops with departures are added to the raptor.
…s from router in service

- Move transfer generation to convert package.
- Pass generators to converter instead of transfers.
- Generator uses a stop collection to build transfers instead of the complete schedule.
- Service catches IllegalArgumentException of raptor router and returns an empty list.
- Introduce GtfsToRaptorTestSchedule to reuse the ManualSchedule.
- Rename isoLines to isolines, be consistent.
@munterfi munterfi marked this pull request as draft January 10, 2025 17:46
@munterfi
Copy link
Member Author

munterfi commented Jan 10, 2025

Hi @clukas1, I have implemented what we discussed in the morning: Only add stops with departures to raptor, catch exceptions from router in service

Reason: In many GTFS there are a lot of parent stops without departures, which would then be added to the raptor, which leads to a lot of copying of the stop arrays.

And some more:

  • Move transfer generation to convert package.
  • Pass generators to converter instead of transfers.
  • Generator uses a stop collection to build transfers instead of the complete schedule.
  • Service catches IllegalArgumentException of raptor router and returns an empty list.
  • Introduce GtfsToRaptorTestSchedule to reuse the ManualSchedule.
  • Rename isoLines to isolines, be consistent.

There are still open TODOs since we should think about the exception handling in the service.

Copy link
Member

@clukas1 clukas1 left a comment

Choose a reason for hiding this comment

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

Some minor improvements needed, mainly I think we can improve how transfers are added to the raptor builder and I would include the fallback to location routing if no stops with departures are included in the request.

@@ -226,7 +212,10 @@ private List<Connection> getConnections(@Nullable ch.naviqore.gtfs.schedule.mode
connections = raptorRouter.routeLatestDeparture(targetStops, sourceStops, TypeMapper.map(config));
}
} catch (IllegalArgumentException e) {
throw new ConnectionRoutingException(e);
log.debug("RaptorRouter exception: {}", e.getMessage());
return List.of();
Copy link
Member

Choose a reason for hiding this comment

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

TODO: Add fallback on location routing?

@@ -334,7 +325,9 @@ public Map<Stop, Connection> getIsoLines(Stop source, LocalDateTime time, TimeTy
raptorRouter.routeIsolines(sourceStops, TypeMapper.map(timeType), TypeMapper.map(config)), null,
config, timeType);
} catch (IllegalArgumentException e) {
throw new ConnectionRoutingException(e);
log.debug(e.getMessage());
Copy link
Member

Choose a reason for hiding this comment

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

Maybe also fallback to location isolines?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe something like this:

@Override
    public Map<Stop, Connection> getIsolines(Stop source, LocalDateTime time, TimeType timeType,
                                             ConnectionQueryConfig config) throws ConnectionRoutingException {
        Map<String, LocalDateTime> sourceStops = getAllChildStopsFromStop(source, time);
        Map<String, ch.naviqore.raptor.Connection> isolines;

        try {
            isolines = raptorRouter.routeIsolines(sourceStops, TypeMapper.map(timeType), TypeMapper.map(config));
        } catch (RaptorAlgorithm.InvalidStopException e) {

            // route from stop location to cover the case where a stop has no departures, but a walk to another
            // stop with departures would be feasible.
            log.debug("{}: {}, trying to route from stop location", e.getClass().getSimpleName(), e.getMessage());
            Map<String, LocalDateTime> sourceLocations = getStopsWithWalkTimeFromLocation(source.getLocation(), time,
                    config.getMaximumWalkingDuration(), timeType);
            isolines = raptorRouter.routeIsolines(sourceLocations, TypeMapper.map(timeType), TypeMapper.map(config));

        } catch (IllegalArgumentException e) {
            throw new ConnectionRoutingException(e);
        }

        return mapToStopConnectionMap(isolines, null, config, timeType);
    }

The solution is relativerly easy for getIsolines, but more complex for the getConnections.

I will try to come up with a seimple solution, this may involve refactoring the cases.

Copy link
Member

Choose a reason for hiding this comment

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

See my suggestion in ebea0b0, I think we using the template method pattern this can actually be handled fairly nicely. However, I think we should add some connection post processing to add the source stop to the first/lastMile walk etc. and also add post processing with the walk calculator if first/lastMile walks are followed by a walk transfer as first or last leg in the raptor connection.

@munterfi
Copy link
Member Author

Thanks @clukas1, nice work! All changes are good from my side.

Currently I am trying to refactor the service using the template method pattern on a separate branch: https://github.com/naviqore/public-transit-service/tree/concept/refactor-service

I will open a PR into this branch as soon as I am ready.

@munterfi munterfi marked this pull request as ready for review January 13, 2025 16:37
@clukas1
Copy link
Member

clukas1 commented Jan 13, 2025

@munterfi Changes here look good, should I approve this PR or should we wait for the template method pattern PR (including correct InvalidStopException handling)?

Regarding your new branch, I've had a look and added a commit mainly with comments but also some suggestions how I think we can handle Invalid Stop Exceptions by falling back to location routing in ebea0b0.

@munterfi
Copy link
Member Author

munterfi commented Jan 14, 2025

Hi @clukas1, thanks! I think we can merge this PR, since it fixes the original issue.

The new branch is mainly a refactoring to simplify the fallback solution with the location search.

@clukas1 clukas1 merged commit df3498c into main Jan 14, 2025
2 checks passed
@clukas1 clukas1 deleted the 158-bug-gtfs-stops-with-no-departures branch January 14, 2025 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: GTFS stops with no departures
2 participants