diff --git a/application/src/main/java/org/opentripplanner/model/OtpTransitService.java b/application/src/main/java/org/opentripplanner/model/OtpTransitService.java index 748c44fa788..b2dfa26b684 100644 --- a/application/src/main/java/org/opentripplanner/model/OtpTransitService.java +++ b/application/src/main/java/org/opentripplanner/model/OtpTransitService.java @@ -3,6 +3,7 @@ import com.google.common.collect.Multimap; import java.util.Collection; import java.util.List; +import java.util.Map; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.transit.model.basic.Notice; @@ -15,6 +16,7 @@ import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.transit.model.site.Pathway; import org.opentripplanner.transit.model.site.PathwayNode; +import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.SiteRepository; @@ -77,4 +79,9 @@ public interface OtpTransitService { * transit services if they are outside the configured 'transitServiceStart' and 'transitServiceEnd' */ boolean hasActiveTransit(); + + /** + * @see org.opentripplanner.transit.service.TimetableRepository#findStopByScheduledStopPoint(FeedScopedId) + */ + Map stopsByScheduledStopPoint(); } diff --git a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index e752b5907a1..ccf3a2242c3 100644 --- a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -280,6 +280,13 @@ public List vehicleParkings() { return vehicleParkings; } + /** + * @see org.opentripplanner.transit.service.TimetableRepository#findStopByScheduledStopPoint(FeedScopedId) + */ + public Map stopsByScheduledStopPoints() { + return stopsByScheduledStopPoints; + } + public OtpTransitService build() { return new OtpTransitServiceImpl(this); } @@ -319,10 +326,6 @@ public void limitServiceDays(ServiceDateInterval periodLimit) { LOG.info("Limiting transit service days to time period complete."); } - public Map getStopsByScheduledStopPoints() { - return stopsByScheduledStopPoints; - } - /** * Find all serviceIds in both CalendarServices and CalendarServiceDates. */ diff --git a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java index 2f735f36435..87d55ca9e96 100644 --- a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java +++ b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java @@ -25,6 +25,7 @@ import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.transit.model.site.Pathway; import org.opentripplanner.transit.model.site.PathwayNode; +import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.SiteRepository; @@ -70,6 +71,7 @@ class OtpTransitServiceImpl implements OtpTransitService { private final Collection trips; private final Collection> flexTrips; + private final Map stopsByScheduledStopPoint; /** * Create a read only version of the {@link OtpTransitService}. @@ -91,6 +93,7 @@ class OtpTransitServiceImpl implements OtpTransitService { this.tripPatterns = immutableList(builder.getTripPatterns().values()); this.trips = immutableList(builder.getTripsById().values()); this.flexTrips = immutableList(builder.getFlexTripsById().values()); + this.stopsByScheduledStopPoint = Collections.unmodifiableMap(builder.stopsByScheduledStopPoints()); } @Override @@ -186,6 +189,14 @@ public boolean hasActiveTransit() { return serviceIds.size() > 0; } + /** + * @see org.opentripplanner.transit.service.TimetableRepository#findStopByScheduledStopPoint(FeedScopedId) + */ + @Override + public Map stopsByScheduledStopPoint() { + return stopsByScheduledStopPoint; + } + /* Private Methods */ /** diff --git a/application/src/main/java/org/opentripplanner/netex/NetexModule.java b/application/src/main/java/org/opentripplanner/netex/NetexModule.java index e5422c5ac77..373893ec585 100644 --- a/application/src/main/java/org/opentripplanner/netex/NetexModule.java +++ b/application/src/main/java/org/opentripplanner/netex/NetexModule.java @@ -96,6 +96,7 @@ public void buildGraph() { // - have operators and notice assignments. timetableRepository.addOperators(otpService.getAllOperators()); timetableRepository.addNoticeAssignments(otpService.getNoticeAssignments()); + timetableRepository.addScheduledStopPointMapping(otpService.stopsByScheduledStopPoint()); AddTransitEntitiesToGraph.addToGraph( otpService, diff --git a/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index ec872cfc3f8..151a8d1551a 100644 --- a/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -536,7 +536,8 @@ private void mapScheduledStopPointsToQuays() { var sspid = idFactory.createId(id); var stopId = idFactory.createId(currentNetexIndex.getQuayIdByStopPointRef().lookup(id)); var stop = Objects.requireNonNull(transitBuilder.getStops().get(stopId)); - transitBuilder.getStopsByScheduledStopPoints().put(sspid, stop); + System.out.printf("%s -> %s%n", sspid, stopId); + transitBuilder.stopsByScheduledStopPoints().put(sspid, stop); }); } diff --git a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index d27fe138ef4..e80ae078b6c 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -603,6 +603,11 @@ public boolean containsTrip(FeedScopedId id) { return this.timetableRepositoryIndex.containsTrip(id); } + @Override + public Optional findStopByScheduledStopPoint(FeedScopedId scheduledStopPoint) { + return timetableRepository.findStopByScheduledStopPoint(scheduledStopPoint); + } + /** * Returns a list of Trips that match the filtering defined in the request. * diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index f97aec093ed..2d2db6d41dc 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -455,9 +455,26 @@ public void addTripPattern(FeedScopedId id, TripPattern tripPattern) { tripPatternForId.put(id, tripPattern); } - public void addScheduledStopPointMapping(FeedScopedId scheduledStopPointRef, FeedScopedId stopId) { - var stop = Objects.requireNonNull(siteRepository.getRegularStop(stopId)); - stopsByScheduledStopPointRefs.put(scheduledStopPointRef, stop); + public void addScheduledStopPointMapping(Map mapping) { + stopsByScheduledStopPointRefs.putAll(mapping); + } + + /** + * Return the stop that is associated with the NeTEx concept of a scheduled stop point. + *

+ * The scheduled stop point which is a "location-independent" stop that schedule systems provide + * which in turn can be later be resolved to an actual stop. + *

+ * This way two schedule systems can use their own IDs for scheduled stop points but the stop (the + * actual physical infrastructure) is the same. + *

+ * SIRI feeds are encouraged to refer to scheduled stop points in an EstimatedCall's stopPointRef + * but the specs are unclear and the reality on the ground very mixed. + * + * @link NeTEx Basecamp discussion + */ + public Optional findStopByScheduledStopPoint(FeedScopedId scheduledStopPoint) { + return Optional.ofNullable(stopsByScheduledStopPointRefs.get(scheduledStopPoint)); } /** diff --git a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java index 6e005b355d1..5d01713c62c 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -327,6 +327,11 @@ List findTripTimeOnDate( */ boolean containsTrip(FeedScopedId id); + /** + * @see TimetableRepository#findStopByScheduledStopPoint(FeedScopedId) + */ + Optional findStopByScheduledStopPoint(FeedScopedId scheduledStopPoint); + /** * Returns a list of {@link RegularStop}s that lay within a bounding box and match the other criteria * in the request object. diff --git a/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java b/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java index 47eb45ad287..46f179161d1 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java @@ -107,7 +107,7 @@ public TripOnServiceDate resolveTripOnServiceDate( public TripOnServiceDate resolveTripOnServiceDate( String serviceJourneyId, - LocalDate serviceDate + @Nullable LocalDate serviceDate ) { if (serviceDate == null) { return null; @@ -157,11 +157,11 @@ public LocalDate resolveServiceDate(FramedVehicleJourneyRefStructure vehicleJour * departure from the first stop, only the Date-part is actually used, and is defined to * represent the actual serviceDate. The time and zone part is ignored. */ - public LocalDate resolveServiceDate(ZonedDateTime originAimedDepartureTime) { + public LocalDate resolveServiceDate(@Nullable ZonedDateTime originAimedDepartureTime) { if (originAimedDepartureTime == null) { return null; } - // This grab the local-date from timestamp passed into OTP ignoring the time and zone + // This grabs the local-date from timestamp passed into OTP ignoring the time and zone // information. An alternative is to use the transit model zone: // 'originAimedDepartureTime.withZoneSameInstant(transitService.getTimeZone())' @@ -172,7 +172,7 @@ public LocalDate resolveServiceDate(ZonedDateTime originAimedDepartureTime) { * Resolve a {@link Trip} by resolving a service journey id from FramedVehicleJourneyRef -> * DatedVehicleJourneyRef. */ - public Trip resolveTrip(FramedVehicleJourneyRefStructure journey) { + public Trip resolveTrip(@Nullable FramedVehicleJourneyRefStructure journey) { if (journey != null) { return resolveTrip(journey.getDatedVehicleJourneyRef()); } @@ -184,10 +184,13 @@ public Trip resolveTrip(String serviceJourneyId) { } /** - * Resolve a {@link RegularStop} from a quay id. + * Resolve a {@link RegularStop} from a scheduled stop point or quay id. + * + * @see org.opentripplanner.transit.service.TimetableRepository#findStopByScheduledStopPoint(FeedScopedId) */ - public RegularStop resolveQuay(String quayRef) { - return transitService.getRegularStop(resolveId(quayRef)); + public RegularStop resolveQuay(String stopPointRef) { + var id = resolveId(stopPointRef); + return transitService.findStopByScheduledStopPoint(id).orElseGet(() -> transitService.getRegularStop(id)); } /**