From 6335f67661cde470e854c973bb3fc250b380eb39 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 15:31:34 +0100 Subject: [PATCH 1/6] Stop scheduled stop points in transit service builder --- .../model/impl/OtpTransitServiceBuilder.java | 8 +++++- .../loader/parser/NetexDocumentParser.java | 28 +++++++++---------- .../loader/parser/ServiceFrameParser.java | 11 ++++---- .../netex/mapping/NetexMapper.java | 12 ++++++++ .../transit/service/TimetableRepository.java | 7 +++++ 5 files changed, 45 insertions(+), 21 deletions(-) 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 2b270e8afa4..e752b5907a1 100644 --- a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -122,6 +122,8 @@ public class OtpTransitServiceBuilder { private final List vehicleParkings = new ArrayList<>(); + private final Map stopsByScheduledStopPoints = new HashMap<>(); + private final DataImportIssueStore issueStore; public OtpTransitServiceBuilder(SiteRepository siteRepository, DataImportIssueStore issueStore) { @@ -317,6 +319,10 @@ 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. */ @@ -449,7 +455,7 @@ private boolean transferTripReferencesDoNotExist(ConstrainedTransfer t) { } /** - * Return {@code true} if the the point is a trip-transfer-point and the trip reference is + * Return {@code true} if the point is a trip-transfer-point and the trip reference is * missing. */ private boolean transferPointTripReferenceDoesNotExist(TransferPoint point) { diff --git a/application/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java b/application/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java index f8058f2df8e..d4e4c22f709 100644 --- a/application/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java +++ b/application/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java @@ -69,24 +69,24 @@ private void parseFrameList(List stopAssignment : stopAssignments.getStopAssignment()) { - if (stopAssignment.getValue() instanceof PassengerStopAssignment) { - var assignment = (PassengerStopAssignment) stopAssignment.getValue(); + if (stopAssignment.getValue() instanceof PassengerStopAssignment assignment) { if (assignment.getQuayRef() == null) { PASSENGER_STOP_ASSIGNMENT_LOGGER.info( @@ -153,9 +153,8 @@ private void parseStopAssignments(StopAssignmentsInFrame_RelStructure stopAssign String stopPointRef = assignment.getScheduledStopPointRef().getValue().getRef(); quayIdByStopPointRef.put(stopPointRef, quayRef); } - } else if (stopAssignment.getValue() instanceof FlexibleStopAssignment) { + } else if (stopAssignment.getValue() instanceof FlexibleStopAssignment assignment) { if (OTPFeature.FlexRouting.isOn()) { - FlexibleStopAssignment assignment = (FlexibleStopAssignment) stopAssignment.getValue(); String flexibleStopPlaceRef = assignment.getFlexibleStopPlaceRef().getRef(); // TODO OTP2 - This check belongs to the mapping or as a separate validation @@ -176,7 +175,7 @@ private void parseStopAssignments(StopAssignmentsInFrame_RelStructure stopAssign } } - private void parseRoutes(RoutesInFrame_RelStructure routes) { + private void parseRoutes(@Nullable RoutesInFrame_RelStructure routes) { if (routes == null) return; for (JAXBElement element : routes.getRoute_()) { 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 c8d4f80af63..ec872cfc3f8 100644 --- a/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -202,11 +202,14 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { mapTripPatterns(serviceIds); mapNoticeAssignments(); + mapScheduledStopPointsToQuays(); mapVehicleParkings(); addEntriesToGroupMapperForPostProcessingLater(); } + + /* PRIVATE METHODS */ private void setupGroupMapping() { @@ -528,6 +531,15 @@ private void addEntriesToGroupMapperForPostProcessingLater() { } } + private void mapScheduledStopPointsToQuays() { + currentNetexIndex.getQuayIdByStopPointRef().localKeys().forEach(id -> { + 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); + }); + } + private void mapVehicleParkings() { var mapper = new VehicleParkingMapper(idFactory, issueStore); currentNetexIndex 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 ff8607f3818..f97aec093ed 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -150,6 +150,8 @@ public class TimetableRepository implements Serializable { private transient TransitAlertService transitAlertService; + private final Map stopsByScheduledStopPointRefs = new HashMap<>(); + @Inject public TimetableRepository(SiteRepository siteRepository, Deduplicator deduplicator) { this.siteRepository = Objects.requireNonNull(siteRepository); @@ -453,6 +455,11 @@ 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); + } + /** * TripPatterns used to be reached through hop edges, but we're not creating on-board transit * vertices/edges anymore. From dbad9b11415f09afb7174fb1364633000661c793 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 17:08:23 +0100 Subject: [PATCH 2/6] Complete mapping of SSP --- .../model/OtpTransitService.java | 7 ++++++ .../model/impl/OtpTransitServiceBuilder.java | 11 +++++---- .../model/impl/OtpTransitServiceImpl.java | 11 +++++++++ .../opentripplanner/netex/NetexModule.java | 1 + .../netex/mapping/NetexMapper.java | 3 ++- .../service/DefaultTransitService.java | 5 ++++ .../transit/service/TimetableRepository.java | 23 ++++++++++++++++--- .../transit/service/TransitService.java | 5 ++++ .../updater/siri/EntityResolver.java | 17 ++++++++------ 9 files changed, 68 insertions(+), 15 deletions(-) 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)); } /** From 935d4eca8118e2c6769bcb85a5e91c0556cc6420 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 17 Jan 2025 15:32:30 +0100 Subject: [PATCH 3/6] Add test for EntityResolver Add test for EntityResolver --- .../model/impl/OtpTransitServiceImpl.java | 3 +- .../loader/parser/ServiceFrameParser.java | 1 - .../netex/mapping/NetexMapper.java | 18 +++--- .../updater/siri/EntityResolver.java | 4 +- .../updater/siri/EntityResolverTest.java | 56 +++++++++++++++++++ 5 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 application/src/test/java/org/opentripplanner/updater/siri/EntityResolverTest.java 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 87d55ca9e96..6f2ec4f4a2f 100644 --- a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java +++ b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceImpl.java @@ -93,7 +93,8 @@ 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()); + this.stopsByScheduledStopPoint = + Collections.unmodifiableMap(builder.stopsByScheduledStopPoints()); } @Override diff --git a/application/src/main/java/org/opentripplanner/netex/loader/parser/ServiceFrameParser.java b/application/src/main/java/org/opentripplanner/netex/loader/parser/ServiceFrameParser.java index 52574ed4f20..4b47a854d55 100644 --- a/application/src/main/java/org/opentripplanner/netex/loader/parser/ServiceFrameParser.java +++ b/application/src/main/java/org/opentripplanner/netex/loader/parser/ServiceFrameParser.java @@ -142,7 +142,6 @@ private void parseStopAssignments(@Nullable StopAssignmentsInFrame_RelStructure for (JAXBElement stopAssignment : stopAssignments.getStopAssignment()) { if (stopAssignment.getValue() instanceof PassengerStopAssignment assignment) { - if (assignment.getQuayRef() == null) { PASSENGER_STOP_ASSIGNMENT_LOGGER.info( "PassengerStopAssignment with empty quay ref is dropped. Assigment: {}", 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 151a8d1551a..2621dc4cc68 100644 --- a/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -208,8 +208,6 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { addEntriesToGroupMapperForPostProcessingLater(); } - - /* PRIVATE METHODS */ private void setupGroupMapping() { @@ -532,13 +530,15 @@ private void addEntriesToGroupMapperForPostProcessingLater() { } private void mapScheduledStopPointsToQuays() { - currentNetexIndex.getQuayIdByStopPointRef().localKeys().forEach(id -> { - var sspid = idFactory.createId(id); - var stopId = idFactory.createId(currentNetexIndex.getQuayIdByStopPointRef().lookup(id)); - var stop = Objects.requireNonNull(transitBuilder.getStops().get(stopId)); - System.out.printf("%s -> %s%n", sspid, stopId); - transitBuilder.stopsByScheduledStopPoints().put(sspid, stop); - }); + currentNetexIndex + .getQuayIdByStopPointRef() + .localKeys() + .forEach(id -> { + var sspid = idFactory.createId(id); + var stopId = idFactory.createId(currentNetexIndex.getQuayIdByStopPointRef().lookup(id)); + var stop = Objects.requireNonNull(transitBuilder.getStops().get(stopId)); + transitBuilder.stopsByScheduledStopPoints().put(sspid, stop); + }); } private void mapVehicleParkings() { 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 46f179161d1..194412a6af5 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java @@ -190,7 +190,9 @@ public Trip resolveTrip(String serviceJourneyId) { */ public RegularStop resolveQuay(String stopPointRef) { var id = resolveId(stopPointRef); - return transitService.findStopByScheduledStopPoint(id).orElseGet(() -> transitService.getRegularStop(id)); + return transitService + .findStopByScheduledStopPoint(id) + .orElseGet(() -> transitService.getRegularStop(id)); } /** diff --git a/application/src/test/java/org/opentripplanner/updater/siri/EntityResolverTest.java b/application/src/test/java/org/opentripplanner/updater/siri/EntityResolverTest.java new file mode 100644 index 00000000000..2b90b771c68 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/updater/siri/EntityResolverTest.java @@ -0,0 +1,56 @@ +package org.opentripplanner.updater.siri; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.SiteRepository; +import org.opentripplanner.transit.service.TimetableRepository; + +class EntityResolverTest { + + private static final TimetableRepositoryForTest TEST_MODEL = TimetableRepositoryForTest.of(); + private static final RegularStop STOP_1 = TEST_MODEL.stop("stop-1").build(); + private static final RegularStop STOP_2 = TEST_MODEL.stop("stop-2").build(); + private static final SiteRepository SITE_REPOSITORY = TEST_MODEL + .siteRepositoryBuilder() + .withRegularStops(List.of(STOP_1, STOP_2)) + .build(); + private static final String FEED_ID = STOP_1.getId().getFeedId(); + private static final FeedScopedId SSP_ID = new FeedScopedId(FEED_ID, "ssp-1"); + + @Test + void resolveScheduledStopPointId() { + var timetableRepository = new TimetableRepository(); + timetableRepository.addScheduledStopPointMapping(Map.of(SSP_ID, STOP_1)); + var transitService = new DefaultTransitService(timetableRepository); + var resolver = new EntityResolver(transitService, FEED_ID); + var stop = resolver.resolveQuay(SSP_ID.getId()); + assertEquals(STOP_1, stop); + } + + @Test + void resolveQuayId() { + var timetableRepository = new TimetableRepository(SITE_REPOSITORY, new Deduplicator()); + var transitService = new DefaultTransitService(timetableRepository); + var resolver = new EntityResolver(transitService, FEED_ID); + var stop = resolver.resolveQuay(STOP_1.getId().getId()); + assertEquals(STOP_1, stop); + } + + @Test + void scheduledStopPointTakesPrecedence() { + var timetableRepository = new TimetableRepository(SITE_REPOSITORY, new Deduplicator()); + var transitService = new DefaultTransitService(timetableRepository); + timetableRepository.addScheduledStopPointMapping(Map.of(SSP_ID, STOP_2)); + var resolver = new EntityResolver(transitService, FEED_ID); + assertEquals(STOP_2, resolver.resolveQuay(SSP_ID.getId())); + assertEquals(STOP_1, resolver.resolveQuay(STOP_1.getId().getId())); + } +} From 8470f8c0b94a868b02be015123ca5bdb5330b830 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 17 Jan 2025 17:31:18 +0100 Subject: [PATCH 4/6] Add test for EntityResolver --- .../transit/service/TimetableRepositoryTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/application/src/test/java/org/opentripplanner/transit/service/TimetableRepositoryTest.java b/application/src/test/java/org/opentripplanner/transit/service/TimetableRepositoryTest.java index 94af8e20a95..f205cca467c 100644 --- a/application/src/test/java/org/opentripplanner/transit/service/TimetableRepositoryTest.java +++ b/application/src/test/java/org/opentripplanner/transit/service/TimetableRepositoryTest.java @@ -3,7 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.opentripplanner.framework.application.OtpFileNames.BUILD_CONFIG_FILENAME; +import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; +import java.util.Map; import org.junit.jupiter.api.Test; import org.opentripplanner.ConstantsForTests; import org.opentripplanner._support.time.ZoneIds; @@ -12,8 +14,10 @@ import org.opentripplanner.model.Timetable; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.test.support.ResourceLoader; +import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.Trip; class TimetableRepositoryTest { @@ -107,4 +111,13 @@ void validateTimeZonesWithExplicitTimeZone() { Timetable timetable = timetableRepositoryIndex.getPatternForTrip(trip).getScheduledTimetable(); assertEquals(20 * 60 - 60 * 60, timetable.getTripTimes(trip).getDepartureTime(0)); } + + @Test + void scheduledStopPoints() { + var repo = new TimetableRepository(); + var sspId = id("ssp-1"); + var stop = TimetableRepositoryForTest.of().stop("stop-1").build(); + repo.addScheduledStopPointMapping(Map.of(sspId, stop)); + assertEquals(stop, repo.findStopByScheduledStopPoint(sspId).get()); + } } From 7759557deafc6530b28bafde21b12f78d31f11e0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 3 Feb 2025 17:56:27 +0100 Subject: [PATCH 5/6] Add Nullable annotation --- .../java/org/opentripplanner/updater/siri/EntityResolver.java | 3 +++ 1 file changed, 3 insertions(+) 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 194412a6af5..0f8e07150d2 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/EntityResolver.java @@ -105,6 +105,7 @@ public TripOnServiceDate resolveTripOnServiceDate( ); } + @Nullable public TripOnServiceDate resolveTripOnServiceDate( String serviceJourneyId, @Nullable LocalDate serviceDate @@ -157,6 +158,7 @@ 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. */ + @Nullable public LocalDate resolveServiceDate(@Nullable ZonedDateTime originAimedDepartureTime) { if (originAimedDepartureTime == null) { return null; @@ -172,6 +174,7 @@ public LocalDate resolveServiceDate(@Nullable ZonedDateTime originAimedDeparture * Resolve a {@link Trip} by resolving a service journey id from FramedVehicleJourneyRef -> * DatedVehicleJourneyRef. */ + @Nullable public Trip resolveTrip(@Nullable FramedVehicleJourneyRefStructure journey) { if (journey != null) { return resolveTrip(journey.getDatedVehicleJourneyRef()); From b69d085125b477a604d5d18c775c16d101b7e2df Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 3 Feb 2025 18:01:17 +0100 Subject: [PATCH 6/6] Don't mutate internal collection --- .../model/impl/OtpTransitServiceBuilder.java | 7 +++++++ .../org/opentripplanner/netex/mapping/NetexMapper.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) 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 ccf3a2242c3..41af51d6f9d 100644 --- a/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -326,6 +326,13 @@ public void limitServiceDays(ServiceDateInterval periodLimit) { LOG.info("Limiting transit service days to time period complete."); } + /** + * Add a mapping from a scheduled stop point to the regular stop. + */ + public void addStopByScheduledStopPoint(FeedScopedId sspid, RegularStop stop) { + stopsByScheduledStopPoints.put(sspid, stop); + } + /** * Find all serviceIds in both CalendarServices and CalendarServiceDates. */ 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 2621dc4cc68..03b900343e9 100644 --- a/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/application/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -537,7 +537,7 @@ 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.stopsByScheduledStopPoints().put(sspid, stop); + transitBuilder.addStopByScheduledStopPoint(sspid, stop); }); }