diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 66162ed6b3a..212c3934dc2 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -14,6 +14,12 @@ on: - master - dev-1.x - dev-2.x +env: + # Since version 3.9.0 of Maven it will automatically understand this environment variable. + # However, as of 2024-11 the latest versions of Ubuntu and Debian were on 3.8.8 so it will take some + # time until we can remove the $MAVEN_ARGS below. + MAVEN_ARGS: "--no-transfer-progress -Dstyle.color=always" + jobs: build-linux: runs-on: ubuntu-latest @@ -46,22 +52,29 @@ jobs: # https://github.com/actions/runner-images/issues/1499 # we set nodePath and npmPath to skip downloading the node binary, which frequently times out run: | - mvn --batch-mode jacoco:prepare-agent test jacoco:report -P prettierCheck -Dprettier.nodePath=node -Dprettier.npmPath=npm - mvn --batch-mode package -Dmaven.test.skip -P prettierSkip + mvn $MAVEN_ARGS jacoco:prepare-agent test jacoco:report -P prettierCheck -Dprettier.nodePath=node -Dprettier.npmPath=npm + mvn $MAVEN_ARGS package -Dmaven.test.skip -P prettierSkip - name: Send coverage data to codecov.io if: github.repository_owner == 'opentripplanner' uses: codecov/codecov-action@v4 with: - files: target/site/jacoco/jacoco.xml token: ${{ secrets.CODECOV_TOKEN }} verbose: true + - name: Upload test results to Codecov + # always upload test results, even when failed + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: "*TEST-*.xml" + - name: Deploy to Github Package Registry if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev-1.x' || github.ref == 'refs/heads/dev-2.x') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mvn --batch-mode deploy --settings maven-settings.xml -DskipTests -DGITHUB_REPOSITORY=$GITHUB_REPOSITORY -P prettierSkip -P deployGitHub + run: mvn $MAVEN_ARGS deploy --settings maven-settings.xml -DskipTests -DGITHUB_REPOSITORY=$GITHUB_REPOSITORY -P prettierSkip -P deployGitHub build-windows: timeout-minutes: 20 @@ -79,7 +92,7 @@ jobs: - name: Configure Windows Pagefile uses: al-cheb/configure-pagefile-action@v1.4 - name: Run tests - run: mvn --batch-mode test -P prettierSkip + run: mvn $MAVEN_ARGS test -P prettierSkip docs: if: github.repository_owner == 'opentripplanner' @@ -192,7 +205,7 @@ jobs: distribution: temurin cache: maven - name: Compile Java code - run: mvn --batch-mode compile -DskipTests -P prettierSkip + run: mvn $MAVEN_ARGS compile -DskipTests -P prettierSkip container-image: if: github.repository_owner == 'opentripplanner' && github.event_name == 'push' && (github.ref == 'refs/heads/dev-2.x' || github.ref == 'refs/heads/master') @@ -237,4 +250,4 @@ jobs: MAVEN_SKIP_ARGS="-P prettierSkip -Dmaven.test.skip=true -Dmaven.source.skip=true" - mvn --batch-mode $MAVEN_SKIP_ARGS package com.google.cloud.tools:jib-maven-plugin:build -Djib.to.tags=latest,$image_version + mvn $MAVEN_ARGS $MAVEN_SKIP_ARGS package com.google.cloud.tools:jib-maven-plugin:build -Djib.to.tags=latest,$image_version diff --git a/application/pom.xml b/application/pom.xml index 73738e772d8..3fc0193d0af 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -23,6 +23,11 @@ otp-utils ${project.version} + + ${project.groupId} + otp-raptor + ${project.version} + diff --git a/application/src/client/index.html b/application/src/client/index.html index b5df7bb65aa..e8872ab33cd 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
diff --git a/application/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java b/application/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java index 50c2dd45340..b67ae85a434 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java @@ -1,12 +1,12 @@ package org.opentripplanner.ext.flex; -import static org.opentripplanner.model.StopTime.MISSING_VALUE; - import org.opentripplanner._support.geometry.Polygons; +import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.utils.time.TimeUtils; public class FlexStopTimesForTest { @@ -18,6 +18,8 @@ public class FlexStopTimesForTest { .build(); private static final RegularStop REGULAR_STOP = TEST_MODEL.stop("stop").build(); + private static final Trip TRIP = TimetableRepositoryForTest.trip("flex").build(); + public static StopTime area(String startTime, String endTime) { return area(AREA_STOP, endTime, startTime); } @@ -27,26 +29,74 @@ public static StopTime area(StopLocation areaStop, String endTime, String startT stopTime.setStop(areaStop); stopTime.setFlexWindowStart(TimeUtils.time(startTime)); stopTime.setFlexWindowEnd(TimeUtils.time(endTime)); + stopTime.setTrip(TRIP); return stopTime; } - public static StopTime regularArrival(String arrivalTime) { - return regularStopTime(TimeUtils.time(arrivalTime), MISSING_VALUE); + /** + * Returns an invalid combination of a flex area and continuous stopping. + */ + public static StopTime areaWithContinuousStopping(String time) { + var st = area(time, time); + st.setFlexContinuousPickup(PickDrop.COORDINATE_WITH_DRIVER); + st.setFlexContinuousDropOff(PickDrop.COORDINATE_WITH_DRIVER); + return st; } - public static StopTime regularStopTime(String arrivalTime, String departureTime) { - return regularStopTime(TimeUtils.time(arrivalTime), TimeUtils.time(departureTime)); + /** + * Returns an invalid combination of a flex area and continuous pick up. + */ + public static StopTime areaWithContinuousPickup(String time) { + var st = area(time, time); + st.setFlexContinuousPickup(PickDrop.COORDINATE_WITH_DRIVER); + return st; } - public static StopTime regularStopTime(int arrivalTime, int departureTime) { + /** + * Returns an invalid combination of a flex area and continuous drop off. + */ + public static StopTime areaWithContinuousDropOff(String time) { + var st = area(time, time); + st.setFlexContinuousDropOff(PickDrop.COORDINATE_WITH_DRIVER); + return st; + } + + public static StopTime regularStop(String arrivalTime, String departureTime) { + return regularStop(TimeUtils.time(arrivalTime), TimeUtils.time(departureTime)); + } + + public static StopTime regularStop(String time) { + return regularStop(TimeUtils.time(time), TimeUtils.time(time)); + } + + public static StopTime regularStopWithContinuousStopping(String time) { + var st = regularStop(TimeUtils.time(time), TimeUtils.time(time)); + st.setFlexContinuousPickup(PickDrop.COORDINATE_WITH_DRIVER); + st.setFlexContinuousDropOff(PickDrop.COORDINATE_WITH_DRIVER); + return st; + } + + public static StopTime regularStopWithContinuousPickup(String time) { + var st = regularStop(TimeUtils.time(time), TimeUtils.time(time)); + st.setFlexContinuousPickup(PickDrop.COORDINATE_WITH_DRIVER); + return st; + } + + public static StopTime regularStopWithContinuousDropOff(String time) { + var st = regularStop(TimeUtils.time(time), TimeUtils.time(time)); + st.setFlexContinuousDropOff(PickDrop.COORDINATE_WITH_DRIVER); + return st; + } + + public static StopTime regularStop(int arrivalTime, int departureTime) { var stopTime = new StopTime(); stopTime.setStop(REGULAR_STOP); stopTime.setArrivalTime(arrivalTime); stopTime.setDepartureTime(departureTime); + stopTime.setTrip(TRIP); return stopTime; } - public static StopTime regularDeparture(String departureTime) { - return regularStopTime(MISSING_VALUE, TimeUtils.time(departureTime)); - } + + } diff --git a/application/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java b/application/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java index e6fddb07cf8..23827b9503b 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; -import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStopTime; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStop; import static org.opentripplanner.street.model._data.StreetModelForTest.V1; import static org.opentripplanner.street.model._data.StreetModelForTest.V2; import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; @@ -19,9 +19,9 @@ class ScheduledFlexPathCalculatorTest { .of(id("123")) .withStopTimes( List.of( - regularStopTime("10:00", "10:01"), + regularStop("10:00", "10:01"), area("10:10", "10:20"), - regularStopTime("10:25", "10:26"), + regularStop("10:25", "10:26"), area("10:40", "10:50") ) ) diff --git a/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripIntegrationTest.java b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripIntegrationTest.java new file mode 100644 index 00000000000..3a8e33cfbf2 --- /dev/null +++ b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripIntegrationTest.java @@ -0,0 +1,252 @@ +package org.opentripplanner.ext.flex.trip; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.opentripplanner.test.support.PolylineAssert.assertThatPolylinesAreEqual; + +import java.time.LocalDateTime; +import java.time.Month; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.locationtech.jts.geom.Coordinate; +import org.opentripplanner.TestOtpModel; +import org.opentripplanner.TestServerContext; +import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.ext.fares.DecorateWithFare; +import org.opentripplanner.ext.flex.FlexIntegrationTestData; +import org.opentripplanner.ext.flex.FlexParameters; +import org.opentripplanner.ext.flex.FlexRouter; +import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.framework.geometry.EncodedPolyline; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.module.ValidateAndInterpolateStopTimesForEachTrip; +import org.opentripplanner.model.GenericLocation; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.routing.algorithm.raptoradapter.router.AdditionalSearchDays; +import org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; +import org.opentripplanner.routing.framework.DebugTimingAggregator; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graphfinder.NearbyStop; +import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.street.model.vertex.StreetLocation; +import org.opentripplanner.street.search.request.StreetSearchRequest; +import org.opentripplanner.street.search.state.State; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; +import org.opentripplanner.transit.model.site.AreaStop; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TimetableRepository; +import org.opentripplanner.utils.time.ServiceDateUtils; + +/** + * This tests that the feed for the Cobb County Flex service is processed correctly. This service + * contains both flex zones but also scheduled stops. Inside the zone, passengers can get on or off + * anywhere, so there it works more like a taxi. + *

+ * This service is not being offered anymore, but we keep the test because others of the same + * type still exist. + */ +class ScheduledDeviatedTripIntegrationTest { + + static Graph graph; + static TimetableRepository timetableRepository; + + float delta = 0.01f; + + @Test + void parseCobbCountyAsScheduledDeviatedTrip() { + var flexTrips = timetableRepository.getAllFlexTrips(); + assertFalse(flexTrips.isEmpty()); + assertEquals(72, flexTrips.size()); + + assertEquals( + Set.of(ScheduledDeviatedTrip.class), + flexTrips.stream().map(FlexTrip::getClass).collect(Collectors.toSet()) + ); + + var trip = getFlexTrip(); + var stop = trip + .getStops() + .stream() + .filter(s -> s.getId().getId().equals("cujv")) + .findFirst() + .orElseThrow(); + assertEquals(33.85465, stop.getLat(), delta); + assertEquals(-84.60039, stop.getLon(), delta); + + var flexZone = trip + .getStops() + .stream() + .filter(s -> s.getId().getId().equals("zone_3")) + .findFirst() + .orElseThrow(); + assertEquals(33.825846635310214, flexZone.getLat(), delta); + assertEquals(-84.63430143459385, flexZone.getLon(), delta); + } + + @Test + void calculateDirectFare() { + OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, true)); + var trip = getFlexTrip(); + + var from = getNearbyStop(trip, "from-stop"); + var to = getNearbyStop(trip, "to-stop"); + + var router = new FlexRouter( + graph, + new DefaultTransitService(timetableRepository), + FlexParameters.defaultValues(), + OffsetDateTime.parse("2021-11-12T10:15:24-05:00").toInstant(), + null, + 1, + 1, + List.of(from), + List.of(to) + ); + + var filter = new DecorateWithFare(graph.getFareService()); + + var itineraries = router + .createFlexOnlyItineraries(false) + .stream() + .peek(filter::decorate) + .toList(); + + var itinerary = itineraries.getFirst(); + + assertFalse(itinerary.getFares().getLegProducts().isEmpty()); + + OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, false)); + } + + /** + * Trips which consist of flex and fixed-schedule stops should work in transit mode. + *

+ * The flex stops will show up as intermediate stops (without a departure/arrival time) but you + * cannot board or alight. + */ + @Test + void flexTripInTransitMode() { + var feedId = timetableRepository.getFeedIds().iterator().next(); + + var serverContext = TestServerContext.createServerContext(graph, timetableRepository); + + // from zone 3 to zone 2 + var from = GenericLocation.fromStopId("Transfer Point for Route 30", feedId, "cujv"); + var to = GenericLocation.fromStopId( + "Zone 1 - PUBLIX Super Market,Zone 1 Collection Point", + feedId, + "yz85" + ); + + var itineraries = getItineraries(from, to, serverContext); + + assertEquals(2, itineraries.size()); + + var itin = itineraries.get(0); + var leg = itin.getLegs().get(0); + + assertEquals("cujv", leg.getFrom().stop.getId().getId()); + assertEquals("yz85", leg.getTo().stop.getId().getId()); + + var intermediateStops = leg.getIntermediateStops(); + assertEquals(1, intermediateStops.size()); + assertEquals("zone_1", intermediateStops.get(0).place.stop.getId().getId()); + + EncodedPolyline legGeometry = EncodedPolyline.encode(leg.getLegGeometry()); + assertThatPolylinesAreEqual( + legGeometry.points(), + "kfsmEjojcOa@eBRKfBfHR|ALjBBhVArMG|OCrEGx@OhAKj@a@tAe@hA]l@MPgAnAgw@nr@cDxCm@t@c@t@c@x@_@~@]pAyAdIoAhG}@lE{AzHWhAtt@t~Aj@tAb@~AXdBHn@FlBC`CKnA_@nC{CjOa@dCOlAEz@E|BRtUCbCQ~CWjD??qBvXBl@kBvWOzAc@dDOx@sHv]aIG?q@@c@ZaB\\mA" + ); + } + + /** + * We add flex trips, that can potentially not have a departure and arrival time, to the trip. + *

+ * Normally these trip times are interpolated/repaired during the graph build but for flex this is + * exactly what we don't want. Here we check that the interpolation process is skipped. + * + * @see ValidateAndInterpolateStopTimesForEachTrip#interpolateStopTimes(List) + */ + @Test + void shouldNotInterpolateFlexTimes() { + var feedId = timetableRepository.getFeedIds().iterator().next(); + var pattern = timetableRepository.getTripPatternForId(new FeedScopedId(feedId, "090z:0:01")); + + assertEquals(3, pattern.numberOfStops()); + + var tripTimes = pattern.getScheduledTimetable().getTripTimes(0); + var arrivalTime = tripTimes.getArrivalTime(1); + + assertEquals(StopTime.MISSING_VALUE, arrivalTime); + } + + @BeforeAll + static void setup() { + TestOtpModel model = FlexIntegrationTestData.cobbFlexGtfs(); + graph = model.graph(); + timetableRepository = model.timetableRepository(); + } + + private static List getItineraries( + GenericLocation from, + GenericLocation to, + OtpServerRequestContext serverContext + ) { + var zoneId = ZoneIds.NEW_YORK; + RouteRequest request = new RouteRequest(); + request.journey().transit().setFilters(List.of(AllowAllTransitFilter.of())); + var dateTime = LocalDateTime.of(2021, Month.DECEMBER, 16, 12, 0).atZone(zoneId); + request.setDateTime(dateTime.toInstant()); + request.setFrom(from); + request.setTo(to); + + var transitStartOfTime = ServiceDateUtils.asStartOfService(request.dateTime(), zoneId); + var additionalSearchDays = AdditionalSearchDays.defaults(dateTime); + var result = TransitRouter.route( + request, + serverContext, + TransitGroupPriorityService.empty(), + transitStartOfTime, + additionalSearchDays, + new DebugTimingAggregator() + ); + + return result.getItineraries(); + } + + private static NearbyStop getNearbyStop(FlexTrip trip, String id) { + // getStops() returns a set of stops and the order doesn't correspond to the stop times + // of the trip + var stopLocation = trip + .getStops() + .stream() + .filter(s -> s instanceof AreaStop) + .findFirst() + .orElseThrow(); + + return new NearbyStop( + stopLocation, + 0, + List.of(), + new State( + new StreetLocation(id, new Coordinate(0, 0), I18NString.of(id)), + StreetSearchRequest.of().build() + ) + ); + } + + private static FlexTrip getFlexTrip() { + var feedId = timetableRepository.getFeedIds().iterator().next(); + var tripId = new FeedScopedId(feedId, "a326c618-d42c-4bd1-9624-c314fbf8ecd8"); + return timetableRepository.getFlexTrip(tripId); + } +} diff --git a/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java index 38f3a1fc2a8..4beefeb271e 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java @@ -1,255 +1,62 @@ package org.opentripplanner.ext.flex.trip; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.opentripplanner.test.support.PolylineAssert.assertThatPolylinesAreEqual; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.areaWithContinuousStopping; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStop; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStopWithContinuousStopping; -import java.time.LocalDateTime; -import java.time.Month; -import java.time.OffsetDateTime; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; -import org.opentripplanner.TestOtpModel; -import org.opentripplanner.TestServerContext; -import org.opentripplanner._support.time.ZoneIds; -import org.opentripplanner.ext.fares.DecorateWithFare; -import org.opentripplanner.ext.flex.FlexIntegrationTestData; -import org.opentripplanner.ext.flex.FlexParameters; -import org.opentripplanner.ext.flex.FlexRouter; -import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.framework.geometry.EncodedPolyline; -import org.opentripplanner.framework.i18n.I18NString; -import org.opentripplanner.graph_builder.module.ValidateAndInterpolateStopTimesForEachTrip; -import org.opentripplanner.model.GenericLocation; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.model.StopTime; -import org.opentripplanner.model.plan.Itinerary; -import org.opentripplanner.routing.algorithm.raptoradapter.router.AdditionalSearchDays; -import org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter; -import org.opentripplanner.routing.api.request.RouteRequest; -import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; -import org.opentripplanner.routing.framework.DebugTimingAggregator; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.graphfinder.NearbyStop; -import org.opentripplanner.standalone.api.OtpServerRequestContext; -import org.opentripplanner.street.model.vertex.StreetLocation; -import org.opentripplanner.street.search.request.StreetSearchRequest; -import org.opentripplanner.street.search.state.State; -import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; -import org.opentripplanner.transit.model.site.AreaStop; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TimetableRepository; -import org.opentripplanner.utils.time.ServiceDateUtils; -/** - * This tests that the feed for the Cobb County Flex service is processed correctly. This service - * contains both flex zones but also scheduled stops. Inside the zone, passengers can get on or off - * anywhere, so there it works more like a taxi. - *

- * Read about the details at: https://www.cobbcounty.org/transportation/cobblinc/routes-and-schedules/flex - */ class ScheduledDeviatedTripTest { - static Graph graph; - static TimetableRepository timetableRepository; - - float delta = 0.01f; - - @Test - void parseCobbCountyAsScheduledDeviatedTrip() { - var flexTrips = timetableRepository.getAllFlexTrips(); - assertFalse(flexTrips.isEmpty()); - assertEquals(72, flexTrips.size()); - - assertEquals( - Set.of(ScheduledDeviatedTrip.class), - flexTrips.stream().map(FlexTrip::getClass).collect(Collectors.toSet()) - ); - - var trip = getFlexTrip(); - var stop = trip - .getStops() - .stream() - .filter(s -> s.getId().getId().equals("cujv")) - .findFirst() - .orElseThrow(); - assertEquals(33.85465, stop.getLat(), delta); - assertEquals(-84.60039, stop.getLon(), delta); - - var flexZone = trip - .getStops() - .stream() - .filter(s -> s.getId().getId().equals("zone_3")) - .findFirst() - .orElseThrow(); - assertEquals(33.825846635310214, flexZone.getLat(), delta); - assertEquals(-84.63430143459385, flexZone.getLon(), delta); - } - - @Test - void calculateDirectFare() { - OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, true)); - var trip = getFlexTrip(); - - var from = getNearbyStop(trip, "from-stop"); - var to = getNearbyStop(trip, "to-stop"); - - var router = new FlexRouter( - graph, - new DefaultTransitService(timetableRepository), - FlexParameters.defaultValues(), - OffsetDateTime.parse("2021-11-12T10:15:24-05:00").toInstant(), - null, - 1, - 1, - List.of(from), - List.of(to) - ); - - var filter = new DecorateWithFare(graph.getFareService()); - - var itineraries = router - .createFlexOnlyItineraries(false) - .stream() - .peek(filter::decorate) - .toList(); - - var itinerary = itineraries.getFirst(); - - assertFalse(itinerary.getFares().getLegProducts().isEmpty()); - - OTPFeature.enableFeatures(Map.of(OTPFeature.FlexRouting, false)); - } - - /** - * Trips which consist of flex and fixed-schedule stops should work in transit mode. - *

- * The flex stops will show up as intermediate stops (without a departure/arrival time) but you - * cannot board or alight. - */ - @Test - void flexTripInTransitMode() { - var feedId = timetableRepository.getFeedIds().iterator().next(); - - var serverContext = TestServerContext.createServerContext(graph, timetableRepository); - - // from zone 3 to zone 2 - var from = GenericLocation.fromStopId("Transfer Point for Route 30", feedId, "cujv"); - var to = GenericLocation.fromStopId( - "Zone 1 - PUBLIX Super Market,Zone 1 Collection Point", - feedId, - "yz85" - ); - - var itineraries = getItineraries(from, to, serverContext); - - assertEquals(2, itineraries.size()); - - var itin = itineraries.get(0); - var leg = itin.getLegs().get(0); - - assertEquals("cujv", leg.getFrom().stop.getId().getId()); - assertEquals("yz85", leg.getTo().stop.getId().getId()); - - var intermediateStops = leg.getIntermediateStops(); - assertEquals(1, intermediateStops.size()); - assertEquals("zone_1", intermediateStops.get(0).place.stop.getId().getId()); - - EncodedPolyline legGeometry = EncodedPolyline.encode(leg.getLegGeometry()); - assertThatPolylinesAreEqual( - legGeometry.points(), - "kfsmEjojcOa@eBRKfBfHR|ALjBBhVArMG|OCrEGx@OhAKj@a@tAe@hA]l@MPgAnAgw@nr@cDxCm@t@c@t@c@x@_@~@]pAyAdIoAhG}@lE{AzHWhAtt@t~Aj@tAb@~AXdBHn@FlBC`CKnA_@nC{CjOa@dCOlAEz@E|BRtUCbCQ~CWjD??qBvXBl@kBvWOzAc@dDOx@sHv]aIG?q@@c@ZaB\\mA" + private static List> isScheduledDeviatedTripCases() { + return List.of( + List.of( + regularStop("10:10"), + area("10:20", "10:30"), + regularStop("10:40"), + area("10:50", "11:00") + ), + List.of( + regularStopWithContinuousStopping("10:10"), + area("10:20", "10:30"), + regularStopWithContinuousStopping("10:40"), + area("10:50", "11:00") + ) ); } - /** - * We add flex trips, that can potentially not have a departure and arrival time, to the trip. - *

- * Normally these trip times are interpolated/repaired during the graph build but for flex this is - * exactly what we don't want. Here we check that the interpolation process is skipped. - * - * @see ValidateAndInterpolateStopTimesForEachTrip#interpolateStopTimes(List) - */ - @Test - void shouldNotInterpolateFlexTimes() { - var feedId = timetableRepository.getFeedIds().iterator().next(); - var pattern = timetableRepository.getTripPatternForId(new FeedScopedId(feedId, "090z:0:01")); - - assertEquals(3, pattern.numberOfStops()); - - var tripTimes = pattern.getScheduledTimetable().getTripTimes(0); - var arrivalTime = tripTimes.getArrivalTime(1); - - assertEquals(StopTime.MISSING_VALUE, arrivalTime); - } - - @BeforeAll - static void setup() { - TestOtpModel model = FlexIntegrationTestData.cobbFlexGtfs(); - graph = model.graph(); - timetableRepository = model.timetableRepository(); + @ParameterizedTest + @MethodSource("isScheduledDeviatedTripCases") + void isScheduledDeviatedTrip(List stopTimes) { + assertTrue(ScheduledDeviatedTrip.isScheduledDeviatedFlexTrip(stopTimes)); } - private static List getItineraries( - GenericLocation from, - GenericLocation to, - OtpServerRequestContext serverContext - ) { - var zoneId = ZoneIds.NEW_YORK; - RouteRequest request = new RouteRequest(); - request.journey().transit().setFilters(List.of(AllowAllTransitFilter.of())); - var dateTime = LocalDateTime.of(2021, Month.DECEMBER, 16, 12, 0).atZone(zoneId); - request.setDateTime(dateTime.toInstant()); - request.setFrom(from); - request.setTo(to); - - var transitStartOfTime = ServiceDateUtils.asStartOfService(request.dateTime(), zoneId); - var additionalSearchDays = AdditionalSearchDays.defaults(dateTime); - var result = TransitRouter.route( - request, - serverContext, - TransitGroupPriorityService.empty(), - transitStartOfTime, - additionalSearchDays, - new DebugTimingAggregator() + private static List> isNotScheduledDeviatedTripCases() { + return List.of( + List.of( + areaWithContinuousStopping("10:10"), + regularStop("10:20", "10:30"), + areaWithContinuousStopping("10:40"), + regularStop("10:50", "11:00") + ), + List.of( + regularStop("10:10"), + regularStop("10:20") + ) ); - - return result.getItineraries(); } - private static NearbyStop getNearbyStop(FlexTrip trip) { - return getNearbyStop(trip, "nearby-stop"); + @ParameterizedTest + @MethodSource("isNotScheduledDeviatedTripCases") + void isNotScheduledDeviatedTrip(List stopTimes) { + assertFalse(ScheduledDeviatedTrip.isScheduledDeviatedFlexTrip(stopTimes)); } - private static NearbyStop getNearbyStop(FlexTrip trip, String id) { - // getStops() returns a set of stops and the order doesn't correspond to the stop times - // of the trip - var stopLocation = trip - .getStops() - .stream() - .filter(s -> s instanceof AreaStop) - .findFirst() - .orElseThrow(); - return new NearbyStop( - stopLocation, - 0, - List.of(), - new State( - new StreetLocation(id, new Coordinate(0, 0), I18NString.of(id)), - StreetSearchRequest.of().build() - ) - ); - } - - private static FlexTrip getFlexTrip() { - var feedId = timetableRepository.getFeedIds().iterator().next(); - var tripId = new FeedScopedId(feedId, "a326c618-d42c-4bd1-9624-c314fbf8ecd8"); - return timetableRepository.getFlexTrip(tripId); - } -} +} \ No newline at end of file diff --git a/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index cfcbc123642..b7f2706d25a 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.ext.flex.FlexStopTimesForTest; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; @@ -45,35 +46,14 @@ class UnscheduledTripTest { @Nested class IsUnscheduledTrip { - private static final StopTime SCHEDULED_STOP = new StopTime(); - private static final StopTime UNSCHEDULED_STOP = new StopTime(); - private static final StopTime CONTINUOUS_PICKUP_STOP = new StopTime(); - private static final StopTime CONTINUOUS_DROP_OFF_STOP = new StopTime(); - - static { - var trip = TimetableRepositoryForTest.trip("flex").build(); - SCHEDULED_STOP.setArrivalTime(30); - SCHEDULED_STOP.setDepartureTime(60); - SCHEDULED_STOP.setStop(AREA_STOP); - SCHEDULED_STOP.setTrip(trip); - - UNSCHEDULED_STOP.setFlexWindowStart(30); - UNSCHEDULED_STOP.setFlexWindowEnd(300); - UNSCHEDULED_STOP.setStop(AREA_STOP); - UNSCHEDULED_STOP.setTrip(trip); - - CONTINUOUS_PICKUP_STOP.setFlexContinuousPickup(PickDrop.COORDINATE_WITH_DRIVER); - CONTINUOUS_PICKUP_STOP.setFlexWindowStart(30); - CONTINUOUS_PICKUP_STOP.setFlexWindowEnd(300); - CONTINUOUS_PICKUP_STOP.setStop(AREA_STOP); - CONTINUOUS_PICKUP_STOP.setTrip(trip); - - CONTINUOUS_DROP_OFF_STOP.setFlexContinuousDropOff(PickDrop.COORDINATE_WITH_DRIVER); - CONTINUOUS_DROP_OFF_STOP.setFlexWindowStart(100); - CONTINUOUS_DROP_OFF_STOP.setFlexWindowEnd(200); - CONTINUOUS_DROP_OFF_STOP.setStop(AREA_STOP); - CONTINUOUS_DROP_OFF_STOP.setTrip(trip); - } + private static final StopTime SCHEDULED_STOP = FlexStopTimesForTest.regularStop("10:00"); + private static final StopTime UNSCHEDULED_STOP = FlexStopTimesForTest.area("10:10", "10:20"); + private static final StopTime CONTINUOUS_PICKUP_STOP = FlexStopTimesForTest.regularStopWithContinuousPickup("10:30"); + private static final StopTime CONTINUOUS_DROP_OFF_STOP = FlexStopTimesForTest.regularStopWithContinuousDropOff("10:40"); + + // disallowed by the GTFS spec + private static final StopTime FLEX_AND_CONTINUOUS_PICKUP_STOP = FlexStopTimesForTest.areaWithContinuousPickup("10:50"); + private static final StopTime FLEX_AND_CONTINUOUS_DROP_OFF_STOP = FlexStopTimesForTest.areaWithContinuousDropOff("11:00"); static List> notUnscheduled() { return List.of( @@ -82,9 +62,9 @@ static List> notUnscheduled() { List.of(SCHEDULED_STOP, SCHEDULED_STOP), List.of(SCHEDULED_STOP, SCHEDULED_STOP, SCHEDULED_STOP), List.of(UNSCHEDULED_STOP, SCHEDULED_STOP, UNSCHEDULED_STOP), - List.of(UNSCHEDULED_STOP, CONTINUOUS_PICKUP_STOP), - List.of(UNSCHEDULED_STOP, CONTINUOUS_DROP_OFF_STOP), - List.of(CONTINUOUS_PICKUP_STOP, CONTINUOUS_DROP_OFF_STOP) + List.of(UNSCHEDULED_STOP, FLEX_AND_CONTINUOUS_PICKUP_STOP), + List.of(UNSCHEDULED_STOP, FLEX_AND_CONTINUOUS_DROP_OFF_STOP), + List.of(FLEX_AND_CONTINUOUS_PICKUP_STOP, FLEX_AND_CONTINUOUS_DROP_OFF_STOP) ); } @@ -101,7 +81,11 @@ static List> unscheduled() { List.of(SCHEDULED_STOP, UNSCHEDULED_STOP), List.of(UNSCHEDULED_STOP, UNSCHEDULED_STOP), List.of(UNSCHEDULED_STOP, UNSCHEDULED_STOP, UNSCHEDULED_STOP), - Collections.nCopies(10, UNSCHEDULED_STOP) + Collections.nCopies(10, UNSCHEDULED_STOP), + List.of(UNSCHEDULED_STOP, CONTINUOUS_PICKUP_STOP), + List.of(CONTINUOUS_PICKUP_STOP, UNSCHEDULED_STOP), + List.of(UNSCHEDULED_STOP, CONTINUOUS_DROP_OFF_STOP), + List.of(CONTINUOUS_DROP_OFF_STOP, UNSCHEDULED_STOP) ); } diff --git a/application/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java b/application/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java index 18d61a3db42..030b7fbfb1c 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java @@ -136,8 +136,8 @@ static void setup() { @Override public List getModesOfStopLocation(StopLocation stop) { - if (stop.getGtfsVehicleType() != null) { - return List.of(stop.getGtfsVehicleType()); + if (stop.getVehicleType() != null) { + return List.of(stop.getVehicleType()); } else { return List.copyOf(modes.get(stop)); } diff --git a/application/src/ext-test/java/org/opentripplanner/ext/restapi/parameter/QualifiedModeSetTest.java b/application/src/ext-test/java/org/opentripplanner/ext/restapi/parameter/QualifiedModeSetTest.java index ad344713a74..7c3bf410192 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/restapi/parameter/QualifiedModeSetTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/restapi/parameter/QualifiedModeSetTest.java @@ -231,7 +231,7 @@ void carHail() { @Test void carHailWithTransit() { var modeSet = new QualifiedModeSet("CAR_HAIL,BUS,RAIL"); - assertEquals(Set.of(COACH, BUS, RAIL), Set.copyOf(modeSet.getTransitModes())); + assertEquals(Set.of(BUS, RAIL), Set.copyOf(modeSet.getTransitModes())); assertEquals(WALK, modeSet.getRequestModes().directMode); assertEquals(CAR_HAILING, modeSet.getRequestModes().accessMode); diff --git a/application/src/ext-test/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceTest.java b/application/src/ext-test/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceTest.java index 761971bc56f..84161c00484 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceTest.java @@ -7,6 +7,7 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.updater.spi.HttpHeaders; class SmooveBikeRentalDataSourceTest { @@ -18,7 +19,8 @@ void makeStation() { "file:src/ext-test/resources/smoovebikerental/smoove.json", null, true, - HttpHeaders.empty() + HttpHeaders.empty(), + RentalPickupType.ALL ) ); assertTrue(source.update()); @@ -84,7 +86,8 @@ void makeStationWithoutOverloading() { "file:src/ext-test/resources/smoovebikerental/smoove.json", null, false, - HttpHeaders.empty() + HttpHeaders.empty(), + RentalPickupType.ALL ) ); assertTrue(source.update()); diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 696c5df4c2a..17ce735a447 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -44,20 +44,20 @@ public class FlexTripsMapper { .withTimePenalty(timePenalty) .build() ); - } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { + } else if (ScheduledDeviatedTrip.isScheduledDeviatedFlexTrip(stopTimes)) { result.add( ScheduledDeviatedTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() ); - } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { + } else if (stopTimes.stream().anyMatch(StopTime::combinesContinuousStoppingWithFlexWindow)) { store.add( - "ContinuousFlexTrip", - "Trip %s contains both flex stops and continuous pick up/drop off. This is an invalid combination: https://github.com/MobilityData/gtfs-flex/issues/70", + "ContinuousFlexStopTime", + "Trip %s contains a stop time which combines flex with continuous pick up/drop off. This is an invalid combination: https://github.com/MobilityData/gtfs-flex/issues/70", trip.getId() ); // result.add(new ContinuousPickupDropOffTrip(trip, stopTimes)); } - //Keep lambda! A method-ref would causes incorrect class and line number to be logged + //Keep lambda! A method-ref would cause incorrect class and line number to be logged //noinspection Convert2MethodRef progress.step(m -> LOG.info(m)); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/template/AbstractFlexTemplate.java b/application/src/ext/java/org/opentripplanner/ext/flex/template/AbstractFlexTemplate.java index 8dbcf4d785e..aeeab84259c 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/template/AbstractFlexTemplate.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/template/AbstractFlexTemplate.java @@ -199,7 +199,7 @@ private FlexAccessEgress createFlexAccessEgress( return null; } - final var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState[0], transferEdges); + final var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState, transferEdges); return finalStateOpt .map(finalState -> { diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/template/FlexDirectPathFactory.java b/application/src/ext/java/org/opentripplanner/ext/flex/template/FlexDirectPathFactory.java index f27a502911f..ae35c262a1e 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/template/FlexDirectPathFactory.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/template/FlexDirectPathFactory.java @@ -113,7 +113,7 @@ private Optional createDirectGraphPath( final State[] afterFlexState = flexEdge.traverse(accessNearbyStop.state); - var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState[0], egress.edges); + var finalStateOpt = EdgeTraverser.traverseEdges(afterFlexState, egress.edges); if (finalStateOpt.isEmpty()) { return Optional.empty(); diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/application/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index c8484532b68..25eac71ebb6 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -1,6 +1,5 @@ package org.opentripplanner.ext.flex.trip; -import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.io.Serializable; @@ -35,7 +34,7 @@ public class ScheduledDeviatedTrip ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); List stopTimes = builder.stopTimes(); - if (!isScheduledFlexTrip(stopTimes)) { + if (!isScheduledDeviatedFlexTrip(stopTimes)) { throw new IllegalArgumentException("Incompatible stopTimes for scheduled flex trip"); } @@ -55,12 +54,10 @@ public static ScheduledDeviatedTripBuilder of(FeedScopedId id) { return new ScheduledDeviatedTripBuilder(id); } - public static boolean isScheduledFlexTrip(List stopTimes) { - Predicate notStopType = Predicate.not(st -> st.getStop() instanceof RegularStop); - Predicate notContinuousStop = stopTime -> - stopTime.getFlexContinuousDropOff() == NONE && stopTime.getFlexContinuousPickup() == NONE; + public static boolean isScheduledDeviatedFlexTrip(List stopTimes) { + Predicate notFixedStop = Predicate.not(st -> st.getStop() instanceof RegularStop); return ( - stopTimes.stream().anyMatch(notStopType) && stopTimes.stream().allMatch(notContinuousStop) + stopTimes.stream().anyMatch(notFixedStop) && stopTimes.stream().noneMatch(StopTime::combinesContinuousStoppingWithFlexWindow) ); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/application/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index b0286541bd2..83fc22c9975 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -1,6 +1,5 @@ package org.opentripplanner.ext.flex.trip; -import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.util.Arrays; @@ -8,7 +7,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; import java.util.stream.Collectors; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.TimePenaltyCalculator; @@ -81,11 +79,9 @@ public static UnscheduledTripBuilder of(FeedScopedId id) { * - One or more stop times with a flexible time window but no fixed stop in between them */ public static boolean isUnscheduledTrip(List stopTimes) { - Predicate hasContinuousStops = stopTime -> - stopTime.getFlexContinuousDropOff() != NONE || stopTime.getFlexContinuousPickup() != NONE; if (stopTimes.isEmpty()) { return false; - } else if (stopTimes.stream().anyMatch(hasContinuousStops)) { + } else if (stopTimes.stream().anyMatch(StopTime::combinesContinuousStoppingWithFlexWindow)) { return false; } else if (N_STOPS.contains(stopTimes.size())) { return stopTimes.stream().anyMatch(StopTime::hasFlexWindow); diff --git a/application/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java b/application/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java index 48f87abf2ab..5880a6969fe 100644 --- a/application/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java +++ b/application/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java @@ -10,7 +10,8 @@ static List list() { of("All OTP debuggers", "org.opentripplanner"), of("OTP request/response", "org.opentripplanner.routing.service.DefaultRoutingService"), of("Raptor request/response", "org.opentripplanner.raptor.RaptorService"), - of("Transfer Optimization", "org.opentripplanner.routing.algorithm.transferoptimization") + of("Transfer Optimization", "org.opentripplanner.routing.algorithm.transferoptimization"), + of("Trip Updates", "org.opentripplanner.updater.trip") ); } diff --git a/application/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java b/application/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java index 5aa834554c7..a04ea004bf0 100644 --- a/application/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java +++ b/application/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java @@ -1,6 +1,8 @@ package org.opentripplanner.ext.smoovebikerental; +import java.util.Set; import javax.annotation.Nullable; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; import org.opentripplanner.updater.vehicle_rental.datasources.params.VehicleRentalDataSourceParameters; @@ -12,7 +14,8 @@ public record SmooveBikeRentalDataSourceParameters( String url, String network, boolean overloadingAllowed, - HttpHeaders httpHeaders + HttpHeaders httpHeaders, + Set rentalPickupTypes ) implements VehicleRentalDataSourceParameters { /** @@ -29,4 +32,9 @@ public String getNetwork(String defaultValue) { public VehicleRentalSourceType sourceType() { return VehicleRentalSourceType.SMOOVE; } + + @Override + public boolean allowRentalType(RentalPickupType rentalPickupType) { + return rentalPickupTypes.contains(rentalPickupType); + } } diff --git a/application/src/ext/java/org/opentripplanner/ext/sorlandsbanen/CoachCostCalculator.java b/application/src/ext/java/org/opentripplanner/ext/sorlandsbanen/CoachCostCalculator.java index 89a3071f975..ba4830f29cf 100644 --- a/application/src/ext/java/org/opentripplanner/ext/sorlandsbanen/CoachCostCalculator.java +++ b/application/src/ext/java/org/opentripplanner/ext/sorlandsbanen/CoachCostCalculator.java @@ -1,10 +1,10 @@ package org.opentripplanner.ext.sorlandsbanen; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.transit.model.basic.TransitMode; diff --git a/application/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java b/application/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java index 03b0acd59cd..dc0fa2868ec 100644 --- a/application/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java +++ b/application/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java @@ -18,6 +18,7 @@ import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdater; import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDataSourceFactory; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,7 +111,9 @@ private static List buildListOfNetworksFr networkName, networkParams.geofencingZones(), // overloadingAllowed - not part of GBFS, not supported here - false + false, + // rentalPickupType not supported + RentalPickupType.ALL ) ); } else { diff --git a/application/src/main/java/org/opentripplanner/api/parameter/ApiRequestMode.java b/application/src/main/java/org/opentripplanner/api/parameter/ApiRequestMode.java index 6e19b106033..7ac2c6f7d86 100644 --- a/application/src/main/java/org/opentripplanner/api/parameter/ApiRequestMode.java +++ b/application/src/main/java/org/opentripplanner/api/parameter/ApiRequestMode.java @@ -12,7 +12,8 @@ public enum ApiRequestMode { TRAM(TransitMode.TRAM), SUBWAY(TransitMode.SUBWAY), RAIL(TransitMode.RAIL), - BUS(TransitMode.BUS, TransitMode.COACH), + BUS(TransitMode.BUS), + COACH(TransitMode.COACH), FERRY(TransitMode.FERRY), CABLE_CAR(TransitMode.CABLE_CAR), GONDOLA(TransitMode.GONDOLA), diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 43a8399e70c..34e9b2f8346 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -52,6 +52,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.PlanConnectionImpl; import org.opentripplanner.apis.gtfs.datafetchers.PlanImpl; import org.opentripplanner.apis.gtfs.datafetchers.QueryTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RentalPlaceTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleTypeImpl; import org.opentripplanner.apis.gtfs.datafetchers.RideHailingEstimateImpl; @@ -123,6 +124,7 @@ protected static GraphQLSchema buildSchema() { ) .type("Node", type -> type.typeResolver(new NodeTypeResolver())) .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) + .type("RentalPlace", type -> type.typeResolver(new RentalPlaceTypeResolver())) .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java index 90db8ef1605..4f7a3f61a57 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/AlertImpl.java @@ -23,6 +23,7 @@ import org.opentripplanner.apis.gtfs.model.StopOnRouteModel; import org.opentripplanner.apis.gtfs.model.StopOnTripModel; import org.opentripplanner.apis.gtfs.model.UnknownModel; +import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.TranslatedString; import org.opentripplanner.routing.alertpatch.EntitySelector; @@ -65,11 +66,11 @@ public DataFetcher alertCause() { public DataFetcher alertDescriptionText() { return environment -> { var alert = getSource(environment); - return alert - .descriptionText() - .or(alert::headerText) - .map(t -> t.toString(environment.getLocale())) - .orElse(FALLBACK_EMPTY_STRING); + var descriptionText = GraphQLUtils.getTranslation( + alert.descriptionText().or(alert::headerText).orElse(null), + environment + ); + return descriptionText != null ? descriptionText : FALLBACK_EMPTY_STRING; }; } @@ -103,11 +104,11 @@ public DataFetcher alertHash() { public DataFetcher alertHeaderText() { return environment -> { var alert = getSource(environment); - return alert - .headerText() - .or(alert::descriptionText) - .map(h -> h.toString(environment.getLocale())) - .orElse(FALLBACK_EMPTY_STRING); + var headerText = GraphQLUtils.getTranslation( + alert.headerText().or(alert::descriptionText).orElse(null), + environment + ); + return headerText != null ? headerText : FALLBACK_EMPTY_STRING; }; } @@ -125,7 +126,7 @@ public DataFetcher alertSeverityLevel() { @Override public DataFetcher alertUrl() { return environment -> - getSource(environment).url().map(u -> u.toString(environment.getLocale())).orElse(null); + GraphQLUtils.getTranslation(getSource(environment).url().orElse(null), environment); } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 8d84c7f0b03..5e892c06368 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -28,6 +28,7 @@ import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; import org.opentripplanner.routing.alternativelegs.AlternativeLegsFilter; +import org.opentripplanner.routing.alternativelegs.NavigationDirection; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.timetable.Trip; @@ -275,8 +276,17 @@ private Leg getSource(DataFetchingEnvironment environment) { return environment.getSource(); } + @Override + public DataFetcher> previousLegs() { + return alternativeLegs(NavigationDirection.PREVIOUS); + } + @Override public DataFetcher> nextLegs() { + return alternativeLegs(NavigationDirection.NEXT); + } + + private DataFetcher> alternativeLegs(NavigationDirection direction) { return environment -> { if (environment.getSource() instanceof ScheduledTransitLeg originalLeg) { var args = new GraphQLTypes.GraphQLLegNextLegsArgs(environment.getArguments()); @@ -311,7 +321,7 @@ public DataFetcher> nextLegs() { environment.getSource(), numberOfLegs, environment.getContext().transitService(), - false, + direction, AlternativeLegsFilter.NO_FILTER, limitToExactOriginStop, limitToExactDestinationStop diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index 9e9d78445f3..fa9623f7c55 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -56,6 +56,7 @@ import org.opentripplanner.routing.graphfinder.PatternAtStop; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; import org.opentripplanner.routing.graphfinder.PlaceType; +import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; @@ -394,7 +395,7 @@ public DataFetcher node() { case "Agency": return transitService.getAgencyForId(FeedScopedId.parse(id)); case "Alert": - return null; //TODO + return transitService.getTransitAlertService().getAlertById(FeedScopedId.parse(id)); case "BikePark": var bikeParkId = FeedScopedId.parse(id); return vehicleParkingService == null @@ -928,6 +929,26 @@ public DataFetcher> vehicleRentalStations() { }; } + @Override + public DataFetcher> vehicleRentalsByBbox() { + return environment -> { + VehicleRentalService vehicleRentalService = environment + .getContext() + .vehicleRentalService(); + + var args = new GraphQLTypes.GraphQLQueryTypeVehicleRentalsByBboxArgs( + environment.getArguments() + ); + + return vehicleRentalService.getVehicleRentalPlacesForEnvelope( + args.getGraphQLMinimumLongitude(), + args.getGraphQLMinimumLatitude(), + args.getGraphQLMaximumLongitude(), + args.getGraphQLMaximumLatitude() + ); + }; + } + @Override public DataFetcher viewer() { return environment -> new Object(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalPlaceTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalPlaceTypeResolver.java new file mode 100644 index 00000000000..9e4e5fdb109 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalPlaceTypeResolver.java @@ -0,0 +1,27 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import graphql.schema.TypeResolver; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; + +public class RentalPlaceTypeResolver implements TypeResolver { + + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment env) { + Object o = env.getObject(); + GraphQLSchema schema = env.getSchema(); + + if (o instanceof VehicleRentalStation) { + return schema.getObjectType("VehicleRentalStation"); + } + + if (o instanceof VehicleRentalVehicle) { + return schema.getObjectType("RentalVehicle"); + } + + return null; + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index dffd892e7ee..f6b32771cc8 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -503,6 +503,8 @@ public interface GraphQLLeg { public DataFetcher pickupType(); + public DataFetcher> previousLegs(); + public DataFetcher realTime(); public DataFetcher realtimeState(); @@ -832,6 +834,8 @@ public interface GraphQLQueryType { public DataFetcher> vehicleRentalStations(); + public DataFetcher> vehicleRentalsByBbox(); + public DataFetcher viewer(); } @@ -842,6 +846,9 @@ public interface GraphQLRealTimeEstimate { public DataFetcher time(); } + /** Rental place union that represents either a VehicleRentalStation or a RentalVehicle */ + public interface GraphQLRentalPlace extends TypeResolver {} + /** Rental vehicle represents a vehicle that belongs to a rental network. */ public interface GraphQLRentalVehicle { public DataFetcher allowPickupNow(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 48d60701a96..b94541d2470 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -75,6 +75,63 @@ public enum GraphQLAgencyAlertType { ROUTE_TYPES, } + public static class GraphQLAlertAlertDescriptionTextArgs { + + private String language; + + public GraphQLAlertAlertDescriptionTextArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + + public static class GraphQLAlertAlertHeaderTextArgs { + + private String language; + + public GraphQLAlertAlertHeaderTextArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + + public static class GraphQLAlertAlertUrlArgs { + + private String language; + + public GraphQLAlertAlertUrlArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + /** Cause of a alert */ public enum GraphQLAlertCauseType { ACCIDENT, @@ -1318,6 +1375,69 @@ public void setGraphQLOriginModesWithParentStation( } } + public static class GraphQLLegPreviousLegsArgs { + + private List destinationModesWithParentStation; + private Integer numberOfLegs; + private List originModesWithParentStation; + + public GraphQLLegPreviousLegsArgs(Map args) { + if (args != null) { + if (args.get("destinationModesWithParentStation") != null) { + this.destinationModesWithParentStation = + ((List) args.get("destinationModesWithParentStation")).stream() + .map(item -> + item instanceof GraphQLTransitMode + ? item + : GraphQLTransitMode.valueOf((String) item) + ) + .map(GraphQLTransitMode.class::cast) + .collect(Collectors.toList()); + } + this.numberOfLegs = (Integer) args.get("numberOfLegs"); + if (args.get("originModesWithParentStation") != null) { + this.originModesWithParentStation = + ((List) args.get("originModesWithParentStation")).stream() + .map(item -> + item instanceof GraphQLTransitMode + ? item + : GraphQLTransitMode.valueOf((String) item) + ) + .map(GraphQLTransitMode.class::cast) + .collect(Collectors.toList()); + } + } + } + + public List getGraphQLDestinationModesWithParentStation() { + return this.destinationModesWithParentStation; + } + + public Integer getGraphQLNumberOfLegs() { + return this.numberOfLegs; + } + + public List getGraphQLOriginModesWithParentStation() { + return this.originModesWithParentStation; + } + + public void setGraphQLDestinationModesWithParentStation( + List destinationModesWithParentStation + ) { + this.destinationModesWithParentStation = destinationModesWithParentStation; + } + + public void setGraphQLNumberOfLegs(Integer numberOfLegs) { + this.numberOfLegs = numberOfLegs; + } + + public void setGraphQLOriginModesWithParentStation( + List originModesWithParentStation + ) { + this.originModesWithParentStation = originModesWithParentStation; + } + } + public static class GraphQLLocalDateRangeInput { private java.time.LocalDate end; @@ -1815,6 +1935,35 @@ public void setGraphQLTransitOnly(Boolean transitOnly) { } } + public static class GraphQLPlanPassThroughViaLocationInput { + + private String label; + private List stopLocationIds; + + public GraphQLPlanPassThroughViaLocationInput(Map args) { + if (args != null) { + this.label = (String) args.get("label"); + this.stopLocationIds = (List) args.get("stopLocationIds"); + } + } + + public String getGraphQLLabel() { + return this.label; + } + + public List getGraphQLStopLocationIds() { + return this.stopLocationIds; + } + + public void setGraphQLLabel(String label) { + this.label = label; + } + + public void setGraphQLStopLocationIds(List stopLocationIds) { + this.stopLocationIds = stopLocationIds; + } + } + public static class GraphQLPlanPreferencesInput { private GraphQLAccessibilityPreferencesInput accessibility; @@ -2048,6 +2197,75 @@ public void setGraphQLTransit(List transi } } + public static class GraphQLPlanViaLocationInput { + + private GraphQLPlanPassThroughViaLocationInput passThrough; + private GraphQLPlanVisitViaLocationInput visit; + + public GraphQLPlanViaLocationInput(Map args) { + if (args != null) { + this.passThrough = + new GraphQLPlanPassThroughViaLocationInput((Map) args.get("passThrough")); + this.visit = new GraphQLPlanVisitViaLocationInput((Map) args.get("visit")); + } + } + + public GraphQLPlanPassThroughViaLocationInput getGraphQLPassThrough() { + return this.passThrough; + } + + public GraphQLPlanVisitViaLocationInput getGraphQLVisit() { + return this.visit; + } + + public void setGraphQLPassThrough(GraphQLPlanPassThroughViaLocationInput passThrough) { + this.passThrough = passThrough; + } + + public void setGraphQLVisit(GraphQLPlanVisitViaLocationInput visit) { + this.visit = visit; + } + } + + public static class GraphQLPlanVisitViaLocationInput { + + private String label; + private java.time.Duration minimumWaitTime; + private List stopLocationIds; + + public GraphQLPlanVisitViaLocationInput(Map args) { + if (args != null) { + this.label = (String) args.get("label"); + this.minimumWaitTime = (java.time.Duration) args.get("minimumWaitTime"); + this.stopLocationIds = (List) args.get("stopLocationIds"); + } + } + + public String getGraphQLLabel() { + return this.label; + } + + public java.time.Duration getGraphQLMinimumWaitTime() { + return this.minimumWaitTime; + } + + public List getGraphQLStopLocationIds() { + return this.stopLocationIds; + } + + public void setGraphQLLabel(String label) { + this.label = label; + } + + public void setGraphQLMinimumWaitTime(java.time.Duration minimumWaitTime) { + this.minimumWaitTime = minimumWaitTime; + } + + public void setGraphQLStopLocationIds(List stopLocationIds) { + this.stopLocationIds = stopLocationIds; + } + } + public enum GraphQLPropulsionType { COMBUSTION, COMBUSTION_DIESEL, @@ -2745,6 +2963,7 @@ public static class GraphQLQueryTypePlanArgs { private List transportModes; private GraphQLInputTriangleInput triangle; private GraphQLInputUnpreferredInput unpreferred; + private List via; private Double waitAtBeginningFactor; private Double waitReluctance; private Integer walkBoardCost; @@ -2826,6 +3045,9 @@ public GraphQLQueryTypePlanArgs(Map args) { this.triangle = new GraphQLInputTriangleInput((Map) args.get("triangle")); this.unpreferred = new GraphQLInputUnpreferredInput((Map) args.get("unpreferred")); + if (args.get("via") != null) { + this.via = (List) args.get("via"); + } this.waitAtBeginningFactor = (Double) args.get("waitAtBeginningFactor"); this.waitReluctance = (Double) args.get("waitReluctance"); this.walkBoardCost = (Integer) args.get("walkBoardCost"); @@ -3057,6 +3279,10 @@ public GraphQLInputUnpreferredInput getGraphQLUnpreferred() { return this.unpreferred; } + public List getGraphQLVia() { + return this.via; + } + public Double getGraphQLWaitAtBeginningFactor() { return this.waitAtBeginningFactor; } @@ -3315,6 +3541,10 @@ public void setGraphQLUnpreferred(GraphQLInputUnpreferredInput unpreferred) { this.unpreferred = unpreferred; } + public void setGraphQLVia(List via) { + this.via = via; + } + public void setGraphQLWaitAtBeginningFactor(Double waitAtBeginningFactor) { this.waitAtBeginningFactor = waitAtBeginningFactor; } @@ -3362,6 +3592,7 @@ public static class GraphQLQueryTypePlanConnectionArgs { private GraphQLPlanLabeledLocationInput origin; private GraphQLPlanPreferencesInput preferences; private java.time.Duration searchWindow; + private List via; public GraphQLQueryTypePlanConnectionArgs(Map args) { if (args != null) { @@ -3380,6 +3611,9 @@ public GraphQLQueryTypePlanConnectionArgs(Map args) { this.preferences = new GraphQLPlanPreferencesInput((Map) args.get("preferences")); this.searchWindow = (java.time.Duration) args.get("searchWindow"); + if (args.get("via") != null) { + this.via = (List) args.get("via"); + } } } @@ -3431,6 +3665,10 @@ public java.time.Duration getGraphQLSearchWindow() { return this.searchWindow; } + public List getGraphQLVia() { + return this.via; + } + public void setGraphQLAfter(String after) { this.after = after; } @@ -3478,6 +3716,10 @@ public void setGraphQLPreferences(GraphQLPlanPreferencesInput preferences) { public void setGraphQLSearchWindow(java.time.Duration searchWindow) { this.searchWindow = searchWindow; } + + public void setGraphQLVia(List via) { + this.via = via; + } } public static class GraphQLQueryTypeRentalVehicleArgs { @@ -3979,6 +4221,55 @@ public void setGraphQLIds(List ids) { } } + public static class GraphQLQueryTypeVehicleRentalsByBboxArgs { + + private Double maximumLatitude; + private Double maximumLongitude; + private Double minimumLatitude; + private Double minimumLongitude; + + public GraphQLQueryTypeVehicleRentalsByBboxArgs(Map args) { + if (args != null) { + this.maximumLatitude = (Double) args.get("maximumLatitude"); + this.maximumLongitude = (Double) args.get("maximumLongitude"); + this.minimumLatitude = (Double) args.get("minimumLatitude"); + this.minimumLongitude = (Double) args.get("minimumLongitude"); + } + } + + public Double getGraphQLMaximumLatitude() { + return this.maximumLatitude; + } + + public Double getGraphQLMaximumLongitude() { + return this.maximumLongitude; + } + + public Double getGraphQLMinimumLatitude() { + return this.minimumLatitude; + } + + public Double getGraphQLMinimumLongitude() { + return this.minimumLongitude; + } + + public void setGraphQLMaximumLatitude(Double maximumLatitude) { + this.maximumLatitude = maximumLatitude; + } + + public void setGraphQLMaximumLongitude(Double maximumLongitude) { + this.maximumLongitude = maximumLongitude; + } + + public void setGraphQLMinimumLatitude(Double minimumLatitude) { + this.minimumLatitude = minimumLatitude; + } + + public void setGraphQLMinimumLongitude(Double minimumLongitude) { + this.minimumLongitude = minimumLongitude; + } + } + public enum GraphQLRealtimeState { ADDED, CANCELED, diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 2a7f2a95ba0..bf6b010bf2c 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -125,4 +125,5 @@ config: FareMedium: org.opentripplanner.model.fare.FareMedium#FareMedium RiderCategory: org.opentripplanner.model.fare.RiderCategory#RiderCategory StopPosition: org.opentripplanner.apis.gtfs.model.StopPosition#StopPosition + RentalPlace: org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace#VehicleRentalPlace diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java index 696ce9c45b5..19bee5e430d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java @@ -54,6 +54,8 @@ public static RouteRequest toRouteRequest( callWith.argument("from", (Map v) -> request.setFrom(toGenericLocation(v))); callWith.argument("to", (Map v) -> request.setTo(toGenericLocation(v))); + mapViaLocations(request, environment); + request.setDateTime( environment.getArgument("date"), environment.getArgument("time"), @@ -255,6 +257,12 @@ public static RouteRequest toRouteRequest( return request; } + static void mapViaLocations(RouteRequest request, DataFetchingEnvironment env) { + var args = env.getArgument("via"); + var locs = ViaLocationMapper.mapToViaLocations((List>>) args); + request.setViaLocations(locs); + } + private static boolean hasArgument(Map m, String name) { return m.containsKey(name) && m.get(name) != null; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java index 95752548ca8..23e42f9a70c 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java @@ -9,6 +9,8 @@ import graphql.schema.DataFetchingEnvironment; import java.time.Instant; +import java.util.List; +import java.util.Map; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.graphql.GraphQLUtils; @@ -63,6 +65,8 @@ public static RouteRequest toRouteRequest( setModes(request.journey(), args.getGraphQLModes(), environment); + // sadly we need to use the raw collection because it is cast to the wrong type + mapViaPoints(request, environment.getArgument("via")); return request; } @@ -178,4 +182,8 @@ private static GenericLocation parseGenericLocation( coordinate.getGraphQLLongitude() ); } + + static void mapViaPoints(RouteRequest request, List>> via) { + request.setViaLocations(ViaLocationMapper.mapToViaLocations(via)); + } } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapper.java new file mode 100644 index 00000000000..984f67855e8 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapper.java @@ -0,0 +1,56 @@ +package org.opentripplanner.apis.gtfs.mapping.routerequest; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.opentripplanner.routing.api.request.via.PassThroughViaLocation; +import org.opentripplanner.routing.api.request.via.ViaLocation; +import org.opentripplanner.routing.api.request.via.VisitViaLocation; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.utils.collection.ListUtils; + +/** + * Maps the input data to the data structure needed for via routing. + */ +class ViaLocationMapper { + + static final String FIELD_STOP_LOCATION_IDS = "stopLocationIds"; + static final String FIELD_LABEL = "label"; + static final String FIELD_MINIMUM_WAIT_TIME = "minimumWaitTime"; + static final String FIELD_VISIT = "visit"; + static final String FIELD_PASS_THROUGH = "passThrough"; + + static List mapToViaLocations(@Nullable List>> via) { + return ListUtils + .nullSafeImmutableList(via) + .stream() + .map(ViaLocationMapper::mapViaLocation) + .toList(); + } + + private static ViaLocation mapViaLocation(Map> via) { + var passThrough = via.get(FIELD_PASS_THROUGH); + var visit = via.get(FIELD_VISIT); + + if (passThrough != null && passThrough.get(FIELD_STOP_LOCATION_IDS) != null) { + return new PassThroughViaLocation( + (String) passThrough.get(FIELD_LABEL), + mapStopLocationIds((List) passThrough.get(FIELD_STOP_LOCATION_IDS)) + ); + } else if (visit != null) { + return new VisitViaLocation( + (String) visit.get(FIELD_LABEL), + (Duration) visit.get(FIELD_MINIMUM_WAIT_TIME), + mapStopLocationIds((List) visit.get(FIELD_STOP_LOCATION_IDS)), + List.of() + ); + } else { + throw new IllegalArgumentException("ViaLocation must define either pass-through or visit."); + } + } + + private static List mapStopLocationIds(List ids) { + return ids.stream().map(FeedScopedId::parse).toList(); + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/framework/InfoLinkType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/framework/InfoLinkType.java index 6541b48a44d..e0472a2ccf8 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/framework/InfoLinkType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/framework/InfoLinkType.java @@ -20,7 +20,7 @@ public static GraphQLObjectType create() { .description("URI") .dataFetcher(environment -> { AlertUrl source = environment.getSource(); - return source.uri; + return source.uri(); }) .build() ) @@ -32,7 +32,7 @@ public static GraphQLObjectType create() { .description("Label") .dataFetcher(environment -> { AlertUrl source = environment.getSource(); - return source.label; + return source.label(); }) .build() ) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java index 5bf56f75e4b..4b405f50f66 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java @@ -33,6 +33,7 @@ import org.opentripplanner.model.plan.TransitLeg; import org.opentripplanner.model.plan.legreference.LegReferenceSerializer; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; +import org.opentripplanner.routing.alternativelegs.NavigationDirection; public class LegType { @@ -485,7 +486,7 @@ public static GraphQLObjectType create( leg, env.getArgument("previous"), GqlUtil.getTransitService(env), - true, + NavigationDirection.PREVIOUS, env.getArgument("filter") ); }) @@ -525,7 +526,7 @@ public static GraphQLObjectType create( leg, env.getArgument("next"), GqlUtil.getTransitService(env), - false, + NavigationDirection.NEXT, env.getArgument("filter") ); }) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/siri/sx/PtSituationElementType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/siri/sx/PtSituationElementType.java index 1567cd977c9..4fd9505b73e 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/siri/sx/PtSituationElementType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/siri/sx/PtSituationElementType.java @@ -241,7 +241,7 @@ public static GraphQLObjectType create( if (!siriUrls.isEmpty()) { return siriUrls; } - return null; + return emptyList(); }) .build() ) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java index 00ac5b0e11b..8dbc6291136 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java @@ -183,7 +183,7 @@ public static GraphQLObjectType create( .dataFetcher(environment -> ((MonoOrMultiModalStation) environment.getSource()).getChildStops() .stream() - .map(StopLocation::getGtfsVehicleType) + .map(StopLocation::getVehicleType) .filter(Objects::nonNull) .collect(Collectors.toSet()) ) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java index 7fcce88c4ee..1ad05118e11 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java @@ -125,14 +125,16 @@ public static GraphQLObjectType create( if (first != null && last != null) { throw new AssertException("Both first and last can't be defined simultaneously."); - } else if (first != null) { - if (first > stops.size()) { - return stops.subList(0, first); - } - } else if (last != null) { - if (last > stops.size()) { - return stops.subList(stops.size() - last, stops.size()); - } + } + + if ((first != null && first < 0) || (last != null && last < 0)) { + throw new AssertException("first and last must be positive integers."); + } + + if (first != null && first < stops.size()) { + return stops.subList(0, first); + } else if (last != null && last < stops.size()) { + return stops.subList(stops.size() - last, stops.size()); } return stops; }) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java index e61d0a12edc..20a290863df 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java @@ -75,13 +75,6 @@ public static GraphQLObjectType create( ) .build() ) - // .field(GraphQLFieldDefinition.newFieldDefinition() - // .name("serviceAlteration") - // .type(serviceAlterationEnum) - // .description("Whether journey is as planned, a cancellation or an extra journey. Default is as planned") - // .dataFetcher(environment -> (((Trip) trip(environment)).getServiceAlteration())) - // .build()) - .field( GraphQLFieldDefinition .newFieldDefinition() @@ -218,14 +211,16 @@ public static GraphQLObjectType create( if (first != null && last != null) { throw new AssertException("Both first and last can't be defined simultaneously."); - } else if (first != null) { - if (first > stops.size()) { - return stops.subList(0, first); - } - } else if (last != null) { - if (last > stops.size()) { - return stops.subList(stops.size() - last, stops.size()); - } + } + + if ((first != null && first < 0) || (last != null && last < 0)) { + throw new AssertException("first and last must be positive integers."); + } + + if (first != null && first < stops.size()) { + return stops.subList(0, first); + } else if (last != null && last < stops.size()) { + return stops.subList(stops.size() - last, stops.size()); } return stops; }) @@ -329,17 +324,6 @@ public static GraphQLObjectType create( ) .build() ) - // .field(GraphQLFieldDefinition.newFieldDefinition() - // .name("keyValues") - // .description("List of keyValue pairs for the service journey.") - // .type(new GraphQLList(keyValueType)) - // .dataFetcher(environment -> ((Trip) trip(environment)).getKeyValues()) - // .build()) - // .field(GraphQLFieldDefinition.newFieldDefinition() - // .name("flexibleServiceType") - // .description("Type of flexible service, or null if service is not flexible.") - // .type(flexibleServiceTypeEnum) - // .build()) .field( GraphQLFieldDefinition .newFieldDefinition() diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 3c8423c5270..f27d252f250 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -1,5 +1,7 @@ package org.opentripplanner.apis.vectortiles; +import static org.opentripplanner.inspector.vector.edge.EdgePropertyMapper.streetPermissionAsString; + import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -30,8 +32,8 @@ import org.opentripplanner.utils.collection.ListUtils; /** - * A Mapbox/Mapblibre style specification for rendering debug information about transit and - * street data. + * A Mapbox/Mapblibre style specification for rendering debug information about transit and street + * data. */ public class DebugStyleSpec { @@ -45,15 +47,20 @@ public class DebugStyleSpec { private static final String MAGENTA = "#f21d52"; private static final String BRIGHT_GREEN = "#22DD9E"; private static final String DARK_GREEN = "#136b04"; + private static final String RED = "#fc0f2a"; private static final String PURPLE = "#BC55F2"; private static final String BLACK = "#140d0e"; private static final int MAX_ZOOM = 23; + private static final int LINE_DETAIL_ZOOM = 13; private static final ZoomDependentNumber LINE_OFFSET = new ZoomDependentNumber( - List.of(new ZoomStop(13, 0.3f), new ZoomStop(MAX_ZOOM, 6)) + List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.4f), new ZoomStop(MAX_ZOOM, 7)) ); private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( - List.of(new ZoomStop(13, 0.2f), new ZoomStop(MAX_ZOOM, 8)) + List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.2f), new ZoomStop(MAX_ZOOM, 8)) + ); + private static final ZoomDependentNumber LINE_HALF_WIDTH = new ZoomDependentNumber( + List.of(new ZoomStop(LINE_DETAIL_ZOOM, 0.1f), new ZoomStop(MAX_ZOOM, 6)) ); private static final ZoomDependentNumber CIRCLE_STROKE = new ZoomDependentNumber( List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) @@ -70,7 +77,15 @@ public class DebugStyleSpec { private static final String EDGES_GROUP = "Edges"; private static final String STOPS_GROUP = "Stops"; private static final String VERTICES_GROUP = "Vertices"; - private static final String TRAVERSAL_PERMISSIONS_GROUP = "Traversal permissions"; + private static final String PERMISSIONS_GROUP = "Permissions"; + private static final String NO_THRU_TRAFFIC_GROUP = "No-thru traffic"; + + private static final StreetTraversalPermission[] streetModes = new StreetTraversalPermission[] { + StreetTraversalPermission.PEDESTRIAN, + StreetTraversalPermission.BICYCLE, + StreetTraversalPermission.CAR, + }; + private static final String WHEELCHAIR_GROUP = "Wheelchair accessibility"; static StyleSpec build( VectorSourceLayer regularStops, @@ -90,6 +105,8 @@ static StyleSpec build( allSources, ListUtils.combine( List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)), + wheelchair(edges), + noThruTraffic(edges), traversalPermissions(edges), edges(edges), vertices(vertices), @@ -183,7 +200,7 @@ private static List edges(VectorSourceLayer edges) { .vectorSourceLayer(edges) .lineColor(MAGENTA) .edgeFilter(EDGES_TO_DISPLAY) - .lineWidth(LINE_WIDTH) + .lineWidth(LINE_HALF_WIDTH) .lineOffset(LINE_OFFSET) .minZoom(6) .maxZoom(MAX_ZOOM) @@ -222,26 +239,32 @@ private static List edges(VectorSourceLayer edges) { private static List traversalPermissions(VectorSourceLayer edges) { var permissionStyles = Arrays - .stream(StreetTraversalPermission.values()) - .map(p -> + .stream(streetModes) + .map(streetTraversalPermission -> StyleBuilder - .ofId(p.name()) + .ofId("permission " + streetTraversalPermission) .vectorSourceLayer(edges) - .group(TRAVERSAL_PERMISSIONS_GROUP) + .group(PERMISSIONS_GROUP) .typeLine() - .lineColor(permissionColor(p)) - .permissionsFilter(p) + .filterValueInProperty( + "permission", + streetTraversalPermission.name(), + StreetTraversalPermission.ALL.name() + ) + .lineCap("butt") + .lineColorMatch("permission", permissionColors(), BLACK) .lineWidth(LINE_WIDTH) .lineOffset(LINE_OFFSET) - .minZoom(6) + .minZoom(LINE_DETAIL_ZOOM) .maxZoom(MAX_ZOOM) .intiallyHidden() ) .toList(); + var textStyle = StyleBuilder .ofId("permission-text") .vectorSourceLayer(edges) - .group(TRAVERSAL_PERMISSIONS_GROUP) + .group(PERMISSIONS_GROUP) .typeSymbol() .lineText("permission") .textOffset(1) @@ -249,12 +272,88 @@ private static List traversalPermissions(VectorSourceLayer edges) .minZoom(17) .maxZoom(MAX_ZOOM) .intiallyHidden(); + return ListUtils.combine(permissionStyles, List.of(textStyle)); } + private static List noThruTraffic(VectorSourceLayer edges) { + var noThruTrafficStyles = Arrays + .stream(streetModes) + .map(streetTraversalPermission -> + StyleBuilder + .ofId("no-thru-traffic " + streetTraversalPermission) + .vectorSourceLayer(edges) + .group(NO_THRU_TRAFFIC_GROUP) + .typeLine() + .filterValueInProperty( + "noThruTraffic", + streetTraversalPermission.name(), + StreetTraversalPermission.ALL.name() + ) + .lineCap("butt") + .lineColorMatch("noThruTraffic", permissionColors(), BLACK) + .lineWidth(LINE_WIDTH) + .lineOffset(LINE_OFFSET) + .minZoom(LINE_DETAIL_ZOOM) + .maxZoom(MAX_ZOOM) + .intiallyHidden() + ) + .toList(); + + var textStyle = StyleBuilder + .ofId("no-thru-traffic-text") + .vectorSourceLayer(edges) + .group(NO_THRU_TRAFFIC_GROUP) + .typeSymbol() + .lineText("noThruTraffic") + .textOffset(1) + .edgeFilter(EDGES_TO_DISPLAY) + .minZoom(17) + .maxZoom(MAX_ZOOM) + .intiallyHidden(); + + return ListUtils.combine(noThruTrafficStyles, List.of(textStyle)); + } + + private static List permissionColors() { + return Arrays + .stream(StreetTraversalPermission.values()) + .flatMap(p -> Stream.of(streetPermissionAsString(p), permissionColor(p))) + .toList(); + } + + private static List wheelchair(VectorSourceLayer edges) { + return List.of( + StyleBuilder + .ofId("wheelchair-accessible") + .vectorSourceLayer(edges) + .group(WHEELCHAIR_GROUP) + .typeLine() + .lineColor(DARK_GREEN) + .booleanFilter("wheelchairAccessible", true) + .lineWidth(LINE_WIDTH) + .lineOffset(LINE_OFFSET) + .minZoom(6) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), + StyleBuilder + .ofId("wheelchair-inaccessible") + .vectorSourceLayer(edges) + .group(WHEELCHAIR_GROUP) + .typeLine() + .lineColor(RED) + .booleanFilter("wheelchairAccessible", false) + .lineWidth(LINE_WIDTH) + .lineOffset(LINE_OFFSET) + .minZoom(6) + .maxZoom(MAX_ZOOM) + .intiallyHidden() + ); + } + private static String permissionColor(StreetTraversalPermission p) { return switch (p) { - case NONE -> "#000"; + case NONE -> BLACK; case PEDESTRIAN -> "#2ba812"; case BICYCLE, PEDESTRIAN_AND_BICYCLE -> "#10d3b6"; case CAR -> "#f92e13"; diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 6f81e7fd998..a70acbb8685 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -10,7 +12,6 @@ import java.util.stream.Stream; import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; import org.opentripplanner.framework.json.ObjectMappers; -import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.utils.collection.ListUtils; @@ -29,7 +30,7 @@ public class StyleBuilder { private final Map layout = new LinkedHashMap<>(); private final Map metadata = new LinkedHashMap<>(); private final Map line = new LinkedHashMap<>(); - private List filter = List.of(); + private List filter = List.of(); public static StyleBuilder ofId(String id) { return new StyleBuilder(id); @@ -167,12 +168,42 @@ public StyleBuilder circleRadius(ZoomDependentNumber radius) { } // Line styling + public StyleBuilder lineCap(String lineCap) { + layout.put("line-cap", lineCap); + return this; + } public StyleBuilder lineColor(String color) { paint.put("line-color", validateColor(color)); return this; } + public StyleBuilder lineColorMatch( + String propertyName, + Collection values, + String defaultValue + ) { + paint.put( + "line-color", + ListUtils.combine( + List.of("match", List.of("get", propertyName)), + (Collection) values, + List.of(defaultValue) + ) + ); + return this; + } + + public StyleBuilder lineOpacity(float lineOpacity) { + paint.put("line-opacity", lineOpacity); + return this; + } + + public StyleBuilder lineDasharray(float... dashArray) { + paint.put("line-dasharray", dashArray); + return this; + } + public StyleBuilder lineWidth(float width) { paint.put("line-width", width); return this; @@ -220,10 +251,10 @@ public final StyleBuilder edgeFilter(Class... classToFilter) { } /** - * Filter the entities by their "permission" property. + * Filter the entities by a boolean property. */ - public final StyleBuilder permissionsFilter(StreetTraversalPermission p) { - filter = List.of("==", "permission", p.name()); + public final StyleBuilder booleanFilter(String propertyName, boolean value) { + filter = List.of("==", propertyName, value); return this; } @@ -235,6 +266,16 @@ public final StyleBuilder vertexFilter(Class... classToFilter) return filterClasses(classToFilter); } + public StyleBuilder filterValueInProperty(String propertyName, String... values) { + var newFilter = new ArrayList<>(); + newFilter.add("any"); + for (String value : values) { + newFilter.add(List.of("in", value, List.of("string", List.of("get", propertyName)))); + } + filter = newFilter; + return this; + } + public JsonNode toJson() { validate(); @@ -257,7 +298,7 @@ public JsonNode toJson() { private StyleBuilder filterClasses(Class... classToFilter) { var clazzes = Arrays.stream(classToFilter).map(Class::getSimpleName).toList(); - filter = ListUtils.combine(List.of("in", "class"), clazzes); + filter = new ArrayList<>(ListUtils.combine(List.of("in", "class"), clazzes)); return this; } diff --git a/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategy.java b/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategy.java deleted file mode 100644 index 0369e3e29db..00000000000 --- a/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategy.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.opentripplanner.astar.strategy; - -import java.util.function.Predicate; -import org.opentripplanner.astar.spi.AStarEdge; -import org.opentripplanner.astar.spi.AStarState; -import org.opentripplanner.astar.spi.SkipEdgeStrategy; - -/** - * Skips edges when the specified number of desired vertices have been visited. - */ -public class MaxCountSkipEdgeStrategy< - State extends AStarState, Edge extends AStarEdge -> - implements SkipEdgeStrategy { - - private final int maxCount; - private final Predicate shouldIncreaseCount; - - private int visited; - - public MaxCountSkipEdgeStrategy(int count, Predicate shouldIncreaseCount) { - this.maxCount = count; - this.shouldIncreaseCount = shouldIncreaseCount; - this.visited = 0; - } - - @Override - public boolean shouldSkipEdge(State current, Edge edge) { - if (shouldIncreaseCount.test(current)) { - visited++; - } - return visited > maxCount; - } -} diff --git a/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategy.java b/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategy.java new file mode 100644 index 00000000000..66c5496c923 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategy.java @@ -0,0 +1,36 @@ +package org.opentripplanner.astar.strategy; + +import java.util.function.Predicate; +import org.opentripplanner.astar.spi.AStarState; +import org.opentripplanner.astar.spi.SearchTerminationStrategy; + +/** + * This termination strategy is used to terminate an a-star search after a number of states matching + * some criteria has been found. For example it can be used to limit a search to a maximum number of + * stops. + */ +public class MaxCountTerminationStrategy> + implements SearchTerminationStrategy { + + private final int maxCount; + private final Predicate shouldIncreaseCount; + private int count; + + /** + * @param maxCount Terminate the search after this many matching states have been reached. + * @param shouldIncreaseCount A predicate to check if a state should increase the count or not. + */ + public MaxCountTerminationStrategy(int maxCount, Predicate shouldIncreaseCount) { + this.maxCount = maxCount; + this.shouldIncreaseCount = shouldIncreaseCount; + this.count = 0; + } + + @Override + public boolean shouldSearchTerminate(State current) { + if (shouldIncreaseCount.test(current)) { + count++; + } + return count >= maxCount; + } +} diff --git a/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 41921d42c62..6631287613b 100644 --- a/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -18,6 +18,17 @@ public enum OTPFeature { APIBikeRental(true, false, "Enable the bike rental endpoint."), APIServerInfo(true, false, "Enable the server info endpoint."), APIUpdaterStatus(true, false, "Enable endpoint for graph updaters status."), + IncludeEmptyRailStopsInTransfers( + false, + false, + """ + Turning this on guarantees that Rail stops without scheduled departures still get included + when generating transfers using `ConsiderPatternsForDirectTransfers`. It is common for stops + to be assign at real-time for Rail. Turning this on will help to avoid dropping transfers which + are needed, when the stop is in use later. Turning this on, if + ConsiderPatternsForDirectTransfers is off has no effect. + """ + ), ConsiderPatternsForDirectTransfers( true, false, diff --git a/application/src/main/java/org/opentripplanner/framework/i18n/I18NString.java b/application/src/main/java/org/opentripplanner/framework/i18n/I18NString.java index 4f75d214c91..101342c7a6f 100644 --- a/application/src/main/java/org/opentripplanner/framework/i18n/I18NString.java +++ b/application/src/main/java/org/opentripplanner/framework/i18n/I18NString.java @@ -1,6 +1,7 @@ package org.opentripplanner.framework.i18n; import java.util.Locale; +import javax.annotation.Nullable; /** * This interface is used when providing translations on server side. Sources: OSM tags with @@ -9,9 +10,20 @@ * @author mabu */ public interface I18NString { - /** true if the given value is not {@code null} or has at least one none white-space character. */ - public static boolean hasValue(I18NString value) { - return value != null && !value.toString().isBlank(); + /** + * Return {@code true} if the given value is not {@code null} or has at least one none + * white-space character. + */ + static boolean hasValue(@Nullable I18NString value) { + return !hasNoValue(value); + } + + /** + * Return {@code true} if the given value has at least one none white-space character. + * Return {@code false} if the value is {@code null} or blank. + */ + static boolean hasNoValue(@Nullable I18NString value) { + return value == null || value.toString().isBlank(); } /** @@ -26,8 +38,8 @@ public static boolean hasValue(I18NString value) { */ String toString(Locale locale); - static I18NString assertHasValue(I18NString value) { - if (value == null || value.toString().isBlank()) { + static I18NString assertHasValue(@Nullable I18NString value) { + if (hasNoValue(value)) { throw new IllegalArgumentException( "Value can not be null, empty or just whitespace: " + (value == null ? "null" : "'" + value + "'") diff --git a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index bc83ad2a8af..bf3447391bd 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -207,7 +207,7 @@ private void addModule(GraphBuilderModule module) { graphBuilderModules.add(module); } - private void addModuleOptional(GraphBuilderModule module) { + private void addModuleOptional(@Nullable GraphBuilderModule module) { if (module != null) { graphBuilderModules.add(module); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/PatternConsideringNearbyStopFinder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/PatternConsideringNearbyStopFinder.java index 9abf759d076..70d1aac3483 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/PatternConsideringNearbyStopFinder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/PatternConsideringNearbyStopFinder.java @@ -1,10 +1,9 @@ package org.opentripplanner.graph_builder.module.nearbystops; -import java.time.Duration; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.routing.api.request.RouteRequest; @@ -49,6 +48,9 @@ public List findNearbyStops( /* Track the closest stop on each flex trip nearby. */ MinMap, NearbyStop> closestStopForFlexTrip = new MinMap<>(); + /* The end result */ + Set uniqueStopsResult = new HashSet<>(); + /* Iterate over nearby stops via the street network or using straight-line distance. */ for (NearbyStop nearbyStop : delegateNearbyStopFinder.findNearbyStops( vertex, @@ -58,9 +60,17 @@ public List findNearbyStops( )) { StopLocation ts1 = nearbyStop.stop; - if (ts1 instanceof RegularStop) { + if (ts1 instanceof RegularStop regularStop) { /* Consider this destination stop as a candidate for every trip pattern passing through it. */ - for (TripPattern pattern : transitService.getPatternsForStop(ts1)) { + Collection patternsForStop = transitService.getPatternsForStop(ts1); + + if (OTPFeature.IncludeEmptyRailStopsInTransfers.isOn()) { + if (patternsForStop.isEmpty() && regularStop.isRailStop()) { + uniqueStopsResult.add(nearbyStop); + } + } + + for (TripPattern pattern : patternsForStop) { if ( reverseDirection ? pattern.canAlight(nearbyStop.stop) @@ -85,10 +95,9 @@ public List findNearbyStops( } /* Make a transfer from the origin stop to each destination stop that was the closest stop on any pattern. */ - Set uniqueStops = new HashSet<>(); - uniqueStops.addAll(closestStopForFlexTrip.values()); - uniqueStops.addAll(closestStopForPattern.values()); + uniqueStopsResult.addAll(closestStopForFlexTrip.values()); + uniqueStopsResult.addAll(closestStopForPattern.values()); // TODO: don't convert to list - return uniqueStops.stream().toList(); + return uniqueStopsResult.stream().toList(); } } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java index e54c27249e1..8277bd47e4c 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java @@ -11,10 +11,8 @@ import java.util.List; import java.util.Set; import org.opentripplanner.astar.model.ShortestPathTree; -import org.opentripplanner.astar.spi.SkipEdgeStrategy; -import org.opentripplanner.astar.strategy.ComposingSkipEdgeStrategy; import org.opentripplanner.astar.strategy.DurationSkipEdgeStrategy; -import org.opentripplanner.astar.strategy.MaxCountSkipEdgeStrategy; +import org.opentripplanner.astar.strategy.MaxCountTerminationStrategy; import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.application.OTPRequestTimeoutException; @@ -30,8 +28,6 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.StreetSearchBuilder; import org.opentripplanner.street.search.TraverseMode; -import org.opentripplanner.street.search.request.StreetSearchRequest; -import org.opentripplanner.street.search.request.StreetSearchRequestMapper; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.strategy.DominanceFunctions; import org.opentripplanner.transit.model.site.AreaStop; @@ -46,8 +42,8 @@ public class StreetNearbyStopFinder implements NearbyStopFinder { /** * Construct a NearbyStopFinder for the given graph and search radius. * - * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the maxStopCount - * we will always return all the directly connected stops. + * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the + * maxStopCount we will always return all the directly connected stops. */ public StreetNearbyStopFinder( Duration durationLimit, @@ -117,23 +113,30 @@ public Collection findNearbyStops( // Return only the origin vertices if there are no valid street modes if ( streetRequest.mode() == StreetMode.NOT_SET || - (maxStopCount != 0 && stopsFound.size() >= maxStopCount) + (maxStopCount > 0 && stopsFound.size() >= maxStopCount) ) { return stopsFound; } stopsFound = new ArrayList<>(stopsFound); - ShortestPathTree spt = StreetSearchBuilder + var streetSearch = StreetSearchBuilder .of() - .setSkipEdgeStrategy(getSkipEdgeStrategy()) + .setSkipEdgeStrategy(new DurationSkipEdgeStrategy<>(durationLimit)) .setDominanceFunction(new DominanceFunctions.MinimumWeight()) .setRequest(request) .setArriveBy(reverseDirection) .setStreetRequest(streetRequest) .setFrom(reverseDirection ? null : originVertices) .setTo(reverseDirection ? originVertices : null) - .setDataOverlayContext(dataOverlayContext) - .getShortestPathTree(); + .setDataOverlayContext(dataOverlayContext); + + if (maxStopCount > 0) { + streetSearch.setTerminationStrategy( + new MaxCountTerminationStrategy<>(maxStopCount, this::hasReachedStop) + ); + } + + ShortestPathTree spt = streetSearch.getShortestPathTree(); // Only used if OTPFeature.FlexRouting.isOn() Multimap locationsMap = ArrayListMultimap.create(); @@ -186,16 +189,6 @@ public Collection findNearbyStops( return stopsFound; } - private SkipEdgeStrategy getSkipEdgeStrategy() { - var durationSkipEdgeStrategy = new DurationSkipEdgeStrategy(durationLimit); - - if (maxStopCount > 0) { - var strategy = new MaxCountSkipEdgeStrategy<>(maxStopCount, this::hasReachedStop); - return new ComposingSkipEdgeStrategy<>(strategy, durationSkipEdgeStrategy); - } - return durationSkipEdgeStrategy; - } - private boolean canBoardFlex(State state, boolean reverse) { Collection edges = reverse ? state.getVertex().getIncoming() @@ -212,13 +205,13 @@ private boolean canBoardFlex(State state, boolean reverse) { *

* This is important because there can be cases where states that cannot actually board the vehicle * can dominate those that can thereby leading to zero found stops when this predicate is used with - * the {@link MaxCountSkipEdgeStrategy}. + * the {@link MaxCountTerminationStrategy}. *

* An example of this would be an egress/reverse search with a very high walk reluctance where the * states that speculatively rent a vehicle move the walk states down the A* priority queue until * the required number of stops are reached to abort the search, leading to zero egress results. */ - public boolean hasReachedStop(State state) { + private boolean hasReachedStop(State state) { var vertex = state.getVertex(); return ( vertex instanceof TransitStopVertex && state.isFinal() && !ignoreVertices.contains(vertex) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java index bb958fd9414..23999d1fec5 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java @@ -688,7 +688,7 @@ private void processSingleWayAreas() { } } try { - newArea(new Area(way, List.of(way), Collections.emptyList(), nodesById)); + addArea(new Area(way, List.of(way), Collections.emptyList(), nodesById)); } catch (Area.AreaConstructionException | Ring.RingConstructionException e) { // this area cannot be constructed, but we already have all the // necessary nodes to construct it. So, something must be wrong with @@ -751,7 +751,7 @@ private void processMultipolygonRelations() { } processedAreas.add(relation); try { - newArea(new Area(relation, outerWays, innerWays, nodesById)); + addArea(new Area(relation, outerWays, innerWays, nodesById)); } catch (Area.AreaConstructionException | Ring.RingConstructionException e) { issueStore.add(new InvalidOsmGeometry(relation)); continue; @@ -786,10 +786,12 @@ private void processMultipolygonRelations() { /** * Handler for a new Area (single way area or multipolygon relations) */ - private void newArea(Area area) { - StreetTraversalPermission permissions = area.parent.overridePermissions( - StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE - ); + private void addArea(Area area) { + StreetTraversalPermission permissions = area.parent + .getOsmProvider() + .getWayPropertySet() + .getDataForWay(area.parent) + .getPermission(); if (area.parent.isRoutable() && permissions != StreetTraversalPermission.NONE) { walkableAreas.add(area); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessor.java index f372f0c82e2..d2bbbe7e27f 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessor.java @@ -312,7 +312,7 @@ private List createArtificialEntra ); } - private VehicleParking createVehicleParkingObjectFromOsmEntity( + VehicleParking createVehicleParkingObjectFromOsmEntity( boolean isCarParkAndRide, Coordinate coordinate, OsmWithTags entity, @@ -421,7 +421,7 @@ private OptionalInt parseCapacity(OsmWithTags element) { } private OptionalInt parseCapacity(OsmWithTags element, String capacityTag) { - return element.getTagAsInt( + return element.parseIntOrBoolean( capacityTag, v -> issueStore.add(new InvalidVehicleParkingCapacity(element, v)) ); diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java index 2f070f272c6..2cb5b01dcc6 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java @@ -86,8 +86,8 @@ void applyWayProperties( boolean motorVehicleNoThrough = tagMapperForWay.isMotorVehicleThroughTrafficExplicitlyDisallowed( way ); - boolean bicycleNoThrough = tagMapperForWay.isBicycleNoThroughTrafficExplicitlyDisallowed(way); - boolean walkNoThrough = tagMapperForWay.isWalkNoThroughTrafficExplicitlyDisallowed(way); + boolean bicycleNoThrough = tagMapperForWay.isBicycleThroughTrafficExplicitlyDisallowed(way); + boolean walkNoThrough = tagMapperForWay.isWalkThroughTrafficExplicitlyDisallowed(way); if (street != null) { double bicycleSafety = wayData.bicycleSafety().forward(); diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java index f27d80d5617..2802ee70a89 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java @@ -499,9 +499,14 @@ private Set createSegments( Area area = intersects.getFirst(); OsmWithTags areaEntity = area.parent; - StreetTraversalPermission areaPermissions = areaEntity.overridePermissions( - StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE - ); + WayProperties wayData; + if (!wayPropertiesCache.containsKey(areaEntity)) { + wayData = areaEntity.getOsmProvider().getWayPropertySet().getDataForWay(areaEntity); + wayPropertiesCache.put(areaEntity, wayData); + } else { + wayData = wayPropertiesCache.get(areaEntity); + } + StreetTraversalPermission areaPermissions = wayData.getPermission(); float carSpeed = areaEntity .getOsmProvider() @@ -520,8 +525,8 @@ private Set createSegments( startEndpoint.getLabel() + " to " + endEndpoint.getLabel(); - I18NString name = namer.getNameForWay(areaEntity, label); + I18NString name = namer.getNameForWay(areaEntity, label); AreaEdgeBuilder streetEdgeBuilder = new AreaEdgeBuilder() .withFromVertex(startEndpoint) .withToVertex(endEndpoint) @@ -543,8 +548,8 @@ private Set createSegments( endEndpoint.getLabel() + " to " + startEndpoint.getLabel(); - name = namer.getNameForWay(areaEntity, label); + name = namer.getNameForWay(areaEntity, label); AreaEdgeBuilder backStreetEdgeBuilder = new AreaEdgeBuilder() .withFromVertex(endEndpoint) .withToVertex(startEndpoint) @@ -559,22 +564,10 @@ private Set createSegments( .withWheelchairAccessible(areaEntity.isWheelchairAccessible()) .withLink(areaEntity.isLink()); - if (!wayPropertiesCache.containsKey(areaEntity)) { - WayProperties wayData = areaEntity - .getOsmProvider() - .getWayPropertySet() - .getDataForWay(areaEntity); - wayPropertiesCache.put(areaEntity, wayData); - } - AreaEdge street = streetEdgeBuilder.buildAndConnect(); + AreaEdge backStreet = backStreetEdgeBuilder.buildAndConnect(); - normalizer.applyWayProperties( - street, - backStreet, - wayPropertiesCache.get(areaEntity), - areaEntity - ); + normalizer.applyWayProperties(street, backStreet, wayData, areaEntity); return Set.of(street, backStreet); } else { // take the part that intersects with the start vertex @@ -640,12 +633,12 @@ private void createNamedAreas(AreaEdgeList edgeList, Ring ring, Collection I18NString name = namer.getNameForWay(areaEntity, id); namedArea.setName(name); + WayProperties wayData; if (!wayPropertiesCache.containsKey(areaEntity)) { - WayProperties wayData = areaEntity - .getOsmProvider() - .getWayPropertySet() - .getDataForWay(areaEntity); + wayData = areaEntity.getOsmProvider().getWayPropertySet().getDataForWay(areaEntity); wayPropertiesCache.put(areaEntity, wayData); + } else { + wayData = wayPropertiesCache.get(areaEntity); } double bicycleSafety = wayPropertiesCache.get(areaEntity).bicycleSafety().forward(); @@ -653,14 +646,8 @@ private void createNamedAreas(AreaEdgeList edgeList, Ring ring, Collection double walkSafety = wayPropertiesCache.get(areaEntity).walkSafety().forward(); namedArea.setWalkSafetyMultiplier(walkSafety); - namedArea.setOriginalEdges(intersection); - - StreetTraversalPermission permission = areaEntity.overridePermissions( - StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE - ); - namedArea.setPermission(permission); - + namedArea.setPermission(wayData.getPermission()); edgeList.addArea(namedArea); } } diff --git a/application/src/main/java/org/opentripplanner/gtfs/mapping/BikeAccessMapper.java b/application/src/main/java/org/opentripplanner/gtfs/mapping/BikeAccessMapper.java index 12f7edcf8c1..4175dabcbc4 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/mapping/BikeAccessMapper.java +++ b/application/src/main/java/org/opentripplanner/gtfs/mapping/BikeAccessMapper.java @@ -5,64 +5,23 @@ import org.opentripplanner.transit.model.network.BikeAccess; /** - * Model bike access for GTFS trips. - *

- * The GTFS bike extensions is originally discussed at: https://groups.google.com/d/msg/gtfs-changes/QqaGOuNmG7o/xyqORy-T4y0J - *

- * It proposes "route_bikes_allowed" in routes.txt and "trip_bikes_allowed" in trips.txt with the - * following semantics: - *

- * 2: bikes allowed
1: no bikes allowed
0: no information (same as field omitted)
- *

- * The values in trips.txt override the values in routes.txt. - *

- * An alternative proposal is discussed in: https://groups.google.com/d/msg/gtfs-changes/rEiSeKNc4cs/gTTnQ_yXtPgJ - *

- * Here, the field "bikes_allowed" is used in both routes.txt and trip.txt with the following - * semantics: - *

- * 2: no bikes allowed
1: bikes allowed
0: no information (same as field omitted)
- *

- * Here, the 0,1,2 semantics have been changed to match the convention used in the - * "wheelchair_accessible" field in trips.txt. - *

- * A number of feeds are still using the original proposal and a number of feeds have been updated - * to use the new proposal. For now, we support both, using "bikes_allowed" if specified and then - * "trip_bikes_allowed". + * Model bike access for GTFS trips by using the bikes_allowed fields from route and trip. */ class BikeAccessMapper { public static BikeAccess mapForTrip(Trip rhs) { - //noinspection deprecation - return mapValues(rhs.getBikesAllowed(), rhs.getTripBikesAllowed()); + return mapValues(rhs.getBikesAllowed()); } public static BikeAccess mapForRoute(Route rhs) { - //noinspection deprecation - return mapValues(rhs.getBikesAllowed(), rhs.getRouteBikesAllowed()); + return mapValues(rhs.getBikesAllowed()); } - private static BikeAccess mapValues(int bikesAllowed, int legacyBikesAllowed) { - if (bikesAllowed != 0) { - switch (bikesAllowed) { - case 1: - return BikeAccess.ALLOWED; - case 2: - return BikeAccess.NOT_ALLOWED; - default: - return BikeAccess.UNKNOWN; - } - } else if (legacyBikesAllowed != 0) { - switch (legacyBikesAllowed) { - case 1: - return BikeAccess.NOT_ALLOWED; - case 2: - return BikeAccess.ALLOWED; - default: - return BikeAccess.UNKNOWN; - } - } - - return BikeAccess.UNKNOWN; + private static BikeAccess mapValues(int bikesAllowed) { + return switch (bikesAllowed) { + case 1 -> BikeAccess.ALLOWED; + case 2 -> BikeAccess.NOT_ALLOWED; + default -> BikeAccess.UNKNOWN; + }; } } diff --git a/application/src/main/java/org/opentripplanner/gtfs/mapping/TransitModeMapper.java b/application/src/main/java/org/opentripplanner/gtfs/mapping/TransitModeMapper.java index 0640011bb3b..919d71455a2 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/mapping/TransitModeMapper.java +++ b/application/src/main/java/org/opentripplanner/gtfs/mapping/TransitModeMapper.java @@ -20,7 +20,7 @@ public static TransitMode mapMode(int routeType) { // Railway Service return TransitMode.RAIL; } else if (routeType >= 200 && routeType < 300) { //Coach Service - return TransitMode.BUS; + return TransitMode.COACH; } else if (routeType >= 300 && routeType < 500) { //Suburban Railway Service and Urban Railway service if (routeType >= 401 && routeType <= 402) { return TransitMode.SUBWAY; diff --git a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 5dc68fc335f..30763edca9e 100644 --- a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -8,6 +8,7 @@ import java.util.List; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.inspector.vector.KeyValue; +import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.edge.StreetEdge; @@ -29,8 +30,10 @@ protected Collection map(Edge input) { private static List mapStreetEdge(StreetEdge se) { var props = Lists.newArrayList( - kv("permission", se.getPermission().toString()), - kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())) + kv("permission", streetPermissionAsString(se.getPermission())), + kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())), + kv("noThruTraffic", noThruTrafficAsString(se)), + kv("wheelchairAccessible", se.isWheelchairAccessible()) ); if (se.hasBogusName()) { props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString()))); @@ -39,4 +42,22 @@ private static List mapStreetEdge(StreetEdge se) { } return props; } + + public static String streetPermissionAsString(StreetTraversalPermission permission) { + return permission.name().replace("_AND_", " "); + } + + private static String noThruTrafficAsString(StreetEdge se) { + var noThruPermission = StreetTraversalPermission.NONE; + if (se.isWalkNoThruTraffic()) { + noThruPermission = noThruPermission.add(StreetTraversalPermission.PEDESTRIAN); + } + if (se.isBicycleNoThruTraffic()) { + noThruPermission = noThruPermission.add(StreetTraversalPermission.BICYCLE); + } + if (se.isMotorVehicleNoThruTraffic()) { + noThruPermission = noThruPermission.add(StreetTraversalPermission.CAR); + } + return streetPermissionAsString(noThruPermission); + } } diff --git a/application/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java b/application/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java index e5bcc6c0322..28fc16a98ac 100644 --- a/application/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java +++ b/application/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java @@ -9,12 +9,19 @@ /** * Represents the real-time update of a single trip. - * @param pattern the pattern to which belongs the updated trip. This can be a new pattern created in real-time. - * @param updatedTripTimes the new trip times for the updated trip. - * @param serviceDate the service date for which this update applies (updates are valid only for one service date) - * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the TripOnServiceDate corresponding to this new trip. - * @param tripCreation true if this update creates a new trip, not present in scheduled data. - * @param routeCreation true if an added trip cannot be registered under an existing route and a new route must be created. + * + * @param pattern the pattern to which belongs the updated trip. This can be a new + * pattern created in real-time. + * @param updatedTripTimes the new trip times for the updated trip. + * @param serviceDate the service date for which this update applies (updates are valid + * only for one service date) + * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the + * TripOnServiceDate corresponding to this new trip. + * @param tripCreation true if this update creates a new trip, not present in scheduled + * data. + * @param routeCreation true if an added trip cannot be registered under an existing route + * and a new route must be created. + * @param producer the producer of the real-time update. */ public record RealTimeTripUpdate( TripPattern pattern, @@ -22,7 +29,8 @@ public record RealTimeTripUpdate( LocalDate serviceDate, @Nullable TripOnServiceDate addedTripOnServiceDate, boolean tripCreation, - boolean routeCreation + boolean routeCreation, + @Nullable String producer ) { public RealTimeTripUpdate { Objects.requireNonNull(pattern); @@ -38,6 +46,25 @@ public RealTimeTripUpdate( TripTimes updatedTripTimes, LocalDate serviceDate ) { - this(pattern, updatedTripTimes, serviceDate, null, false, false); + this(pattern, updatedTripTimes, serviceDate, null, false, false, null); + } + + public RealTimeTripUpdate( + TripPattern pattern, + TripTimes updatedTripTimes, + LocalDate serviceDate, + @Nullable TripOnServiceDate addedTripOnServiceDate, + boolean tripCreation, + boolean routeCreation + ) { + this( + pattern, + updatedTripTimes, + serviceDate, + addedTripOnServiceDate, + tripCreation, + routeCreation, + null + ); } } diff --git a/application/src/main/java/org/opentripplanner/model/StopTime.java b/application/src/main/java/org/opentripplanner/model/StopTime.java index e31350192e1..0754dbce671 100644 --- a/application/src/main/java/org/opentripplanner/model/StopTime.java +++ b/application/src/main/java/org/opentripplanner/model/StopTime.java @@ -1,6 +1,8 @@ /* This file is based on code copied from project OneBusAway, see the LICENSE file for further information. */ package org.opentripplanner.model; +import static org.opentripplanner.model.PickDrop.NONE; + import java.util.List; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.site.StopLocation; @@ -305,4 +307,16 @@ private static int getAvailableTime(int... times) { public boolean hasFlexWindow() { return flexWindowStart != MISSING_VALUE || flexWindowEnd != MISSING_VALUE; } + + /** + * Checks if this stop time combines flex windows with continuous stopping, which is against the + * GTFS spec. + */ + public boolean combinesContinuousStoppingWithFlexWindow() { + return hasContinuousStopping() && hasFlexWindow(); + } + + public boolean hasContinuousStopping() { + return this.flexContinuousPickup != NONE || flexContinuousDropOff != NONE; + } } diff --git a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index 0a54fc964d8..94b490c48a0 100644 --- a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -347,8 +347,7 @@ public Result update(RealTimeTripUpdate realTimeTrip } // The time tables are finished during the commit - - return Result.success(UpdateSuccess.noWarnings()); + return Result.success(UpdateSuccess.noWarnings(realTimeTripUpdate.producer())); } /** diff --git a/application/src/main/java/org/opentripplanner/model/plan/Itinerary.java b/application/src/main/java/org/opentripplanner/model/plan/Itinerary.java index f707b97c131..5ff936f3118 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/Itinerary.java +++ b/application/src/main/java/org/opentripplanner/model/plan/Itinerary.java @@ -17,8 +17,8 @@ import org.opentripplanner.model.SystemNotice; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.path.PathStringBuilder; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.preference.ItineraryFilterPreferences; import org.opentripplanner.utils.lang.DoubleUtils; diff --git a/application/src/main/java/org/opentripplanner/model/projectinfo/OtpProjectInfo.java b/application/src/main/java/org/opentripplanner/model/projectinfo/OtpProjectInfo.java index 37651d4522f..c49fbc1ae4f 100644 --- a/application/src/main/java/org/opentripplanner/model/projectinfo/OtpProjectInfo.java +++ b/application/src/main/java/org/opentripplanner/model/projectinfo/OtpProjectInfo.java @@ -80,7 +80,7 @@ public String toString() { * dev-2.x} */ public String getVersionString() { - String format = "version: %s, ser.ver.id: %s, commit: %s, branch: %s"; + String format = "Version: %s, ser.ver.id: %s, commit: %s, branch: %s"; return String.format( format, version.version, @@ -91,8 +91,8 @@ public String getVersionString() { } /** - * This method compare the maven project version, an return {@code true} if both are the same. Two - * different SNAPSHOT versions are considered the same - work in progress. + * This method compares the maven project version, and return {@code true} if both are the same. + * Two different SNAPSHOT versions are considered the same version - they are work in progress. */ public boolean sameVersion(OtpProjectInfo other) { return this.version.sameVersion(other.version); @@ -100,8 +100,8 @@ public boolean sameVersion(OtpProjectInfo other) { /** * The OTP Serialization version id is used to determine if OTP and a serialized blob(Graph.obj) - * of the otp internal model are compatible. This filed is writen into the Graph.obj file header - * and checked when loading the graph later. + * of the otp internal model are compatible. This field is written into the Graph.obj + * file header and checked when loading the graph later. */ public String getOtpSerializationVersionId() { return graphFileHeaderInfo.otpSerializationVersionId(); diff --git a/application/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java b/application/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java index 1bbe76c0e7c..f53eb67b01a 100644 --- a/application/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java +++ b/application/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java @@ -140,7 +140,7 @@ List findStopsInFlexArea( List stops = stopsSpatialIndex .query(geometry.getEnvelopeInternal()) .stream() - .filter(stop -> flexibleStopTransitMode == stop.getGtfsVehicleType()) + .filter(stop -> flexibleStopTransitMode == stop.getVehicleType()) .filter(stop -> geometry.contains(stop.getGeometry())) .toList(); diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index 3c9a3f537ee..67f737e4c79 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -49,6 +49,7 @@ public class OsmWithTags { private static final Set LEVEL_TAGS = Set.of("level", "layer"); private static final Set DEFAULT_LEVEL = Set.of("0"); + private static final Consumer NO_OP = i -> {}; /* To save memory this is only created when an entity actually has tags. */ private Map tags; @@ -220,6 +221,33 @@ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { return OptionalInt.empty(); } + /** + * Some tags are allowed to have values like 55, "true" or "false". + *

+ * "true", "yes" is returned as 1. + *

+ * "false", "no" is returned as 0 + *

+ * Everything else is returned as an emtpy optional. + */ + public OptionalInt parseIntOrBoolean(String tag, Consumer errorHandler) { + var maybeInt = getTagAsInt(tag, NO_OP); + if (maybeInt.isPresent()) { + return maybeInt; + } else { + if (isTagTrue(tag)) { + return OptionalInt.of(1); + } else if (isTagFalse(tag)) { + return OptionalInt.of(0); + } else if (hasTag(tag)) { + errorHandler.accept(getTag(tag)); + return OptionalInt.empty(); + } else { + return OptionalInt.empty(); + } + } + } + /** * Checks is a tag contains the specified value. */ diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/AtlantaMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/AtlantaMapper.java index 4d190c0b667..34ba62a1daa 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/AtlantaMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/AtlantaMapper.java @@ -13,10 +13,9 @@ * * @author demory * @see OsmTagMapper - * @see DefaultMapper */ -class AtlantaMapper implements OsmTagMapper { +class AtlantaMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -27,7 +26,6 @@ public void populateProperties(WayPropertySet props) { // Max speed limit in Georgia is 70 mph ~= 113kmh ~= 31.3m/s props.maxPossibleCarSpeed = 31.4f; - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapper.java index 9f1b1ac0ade..e2ecb998159 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapper.java @@ -6,7 +6,7 @@ /** * OSM way properties for optimizing distance (not traveling time) in routing. */ -class ConstantSpeedFinlandMapper implements OsmTagMapper { +class ConstantSpeedFinlandMapper extends FinlandMapper { private float speed; @@ -23,8 +23,7 @@ public ConstantSpeedFinlandMapper(float speed) { @Override public void populateProperties(WayPropertySet props) { props.setCarSpeed("highway=*", speed); - // Read the rest from the default set - new FinlandMapper().populateProperties(props); + super.populateProperties(props); props.maxPossibleCarSpeed = speed; } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/DefaultMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/DefaultMapper.java deleted file mode 100644 index faa666c750a..00000000000 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/DefaultMapper.java +++ /dev/null @@ -1,721 +0,0 @@ -package org.opentripplanner.osm.tagmapping; - -import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofBicycleSafety; -import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofWalkSafety; -import static org.opentripplanner.osm.wayproperty.WayPropertiesBuilder.withModes; -import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; -import static org.opentripplanner.street.model.StreetTraversalPermission.BICYCLE_AND_CAR; -import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; -import static org.opentripplanner.street.model.StreetTraversalPermission.NONE; -import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; -import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; - -import org.opentripplanner.osm.wayproperty.WayProperties; -import org.opentripplanner.osm.wayproperty.WayPropertySet; -import org.opentripplanner.osm.wayproperty.specifier.BestMatchSpecifier; -import org.opentripplanner.osm.wayproperty.specifier.LogicalOrSpecifier; -import org.opentripplanner.routing.services.notes.StreetNotesService; - -/** - * This factory class provides a default collection of {@link WayProperties} that determine how OSM - * streets can be traversed in various modes. - *

- * Circa January 2011, Grant and Mele at TriMet undertook proper testing of bike (and transit) - * routing, and worked with David Turner on assigning proper weights to different facility types. - * The weights in this file grew organically from trial and error, and are the result of months of - * testing and tweaking the routes that OTP returned, as well as actually walking/biking these - * routes and making changes based on those experiences. This set of weights should be a great - * starting point for others to use, but they are to some extent tailored to the situation in - * Portland and people shouldn't hesitate to adjust them to for their own instance. - *

- * The rules for assigning WayProperties to OSM ways are explained in. The final tie breaker if two - * Pickers both match is the sequence that the properties are added in this file: if all else is - * equal the 'props.setProperties' statement that is closer to the top of the page will prevail over - * those lower down the page. - *

- * Foot and bicycle permissions are also addressed in OpenStreetMapGraphBuilderImpl.Handler#getPermissionsForEntity(). - * For instance, if a way that normally does not permit walking based on its tag matches (the - * prevailing 'props.setProperties' statement) has a 'foot=yes' tag the permissions are overridden - * and walking is allowed on that way. - *

- * TODO clarify why this needs a separate factory interface. - * - * @author bdferris, novalis - * @see OsmTagMapper - */ -class DefaultMapper implements OsmTagMapper { - - /* Populate properties on existing WayPropertySet */ - public void populateProperties(WayPropertySet props) { - WayProperties allWayProperties = withModes(ALL).build(); - WayProperties noneWayProperties = withModes(NONE).build(); - WayProperties pedestrianWayProperties = withModes(PEDESTRIAN).build(); - WayProperties pedestrianAndBicycleWayProperties = withModes(PEDESTRIAN_AND_BICYCLE).build(); - /* no bicycle tags */ - - /* NONE */ - props.setProperties("mtb:scale=3", noneWayProperties); - props.setProperties("mtb:scale=4", noneWayProperties); - props.setProperties("mtb:scale=5", noneWayProperties); - props.setProperties("mtb:scale=6", noneWayProperties); - - /* PEDESTRIAN */ - props.setProperties("highway=corridor", pedestrianWayProperties); - props.setProperties("highway=steps", pedestrianWayProperties); - props.setProperties("highway=crossing", pedestrianWayProperties); - props.setProperties("highway=platform", pedestrianWayProperties); - props.setProperties("public_transport=platform", pedestrianWayProperties); - props.setProperties("railway=platform", pedestrianWayProperties); - props.setProperties("footway=sidewalk;highway=footway", pedestrianWayProperties); - props.setProperties("mtb:scale=1", pedestrianWayProperties); - props.setProperties("mtb:scale=2", pedestrianWayProperties); - - /* PEDESTRIAN_AND_BICYCLE */ - props.setProperties("mtb:scale=0", pedestrianAndBicycleWayProperties); - props.setProperties("highway=cycleway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.6)); - props.setProperties("highway=path", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75)); - props.setProperties("highway=pedestrian", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.9)); - props.setProperties("highway=footway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1)); - props.setProperties("highway=bridleway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.3)); - - /* ALL */ - props.setProperties("highway=living_street", withModes(ALL).bicycleSafety(0.9)); - props.setProperties("highway=unclassified", allWayProperties); - props.setProperties("highway=road", allWayProperties); - props.setProperties("highway=byway", withModes(ALL).bicycleSafety(1.3)); - props.setProperties("highway=track", withModes(ALL).bicycleSafety(1.3)); - props.setProperties("highway=service", withModes(ALL).bicycleSafety(1.1)); - props.setProperties("highway=residential", withModes(ALL).bicycleSafety(0.98)); - props.setProperties("highway=residential_link", withModes(ALL).bicycleSafety(0.98)); - props.setProperties("highway=tertiary", allWayProperties); - props.setProperties("highway=tertiary_link", allWayProperties); - props.setProperties("highway=secondary", withModes(ALL).bicycleSafety(1.5)); - props.setProperties("highway=secondary_link", withModes(ALL).bicycleSafety(1.5)); - props.setProperties("highway=primary", withModes(ALL).bicycleSafety(2.06)); - props.setProperties("highway=primary_link", withModes(ALL).bicycleSafety(2.06)); - - /* DRIVING ONLY */ - // trunk and motorway links are often short distances and necessary connections - props.setProperties("highway=trunk_link", withModes(CAR).bicycleSafety(2.06)); - props.setProperties("highway=motorway_link", withModes(CAR).bicycleSafety(2.06)); - - props.setProperties("highway=trunk", withModes(CAR).bicycleSafety(7.47)); - props.setProperties("highway=motorway", withModes(CAR).bicycleSafety(8)); - - /* cycleway=lane */ - props.setProperties( - "highway=*;cycleway=lane", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.87) - ); - props.setProperties("highway=service;cycleway=lane", withModes(ALL).bicycleSafety(0.77)); - props.setProperties("highway=residential;cycleway=lane", withModes(ALL).bicycleSafety(0.77)); - props.setProperties( - "highway=residential_link;cycleway=lane", - withModes(ALL).bicycleSafety(0.77) - ); - props.setProperties("highway=tertiary;cycleway=lane", withModes(ALL).bicycleSafety(0.87)); - props.setProperties("highway=tertiary_link;cycleway=lane", withModes(ALL).bicycleSafety(0.87)); - props.setProperties("highway=secondary;cycleway=lane", withModes(ALL).bicycleSafety(0.96)); - props.setProperties("highway=secondary_link;cycleway=lane", withModes(ALL).bicycleSafety(0.96)); - props.setProperties("highway=primary;cycleway=lane", withModes(ALL).bicycleSafety(1.15)); - props.setProperties("highway=primary_link;cycleway=lane", withModes(ALL).bicycleSafety(1.15)); - - /* BICYCLE_AND_CAR */ - props.setProperties( - "highway=trunk;cycleway=lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.5) - ); - props.setProperties( - "highway=trunk_link;cycleway=lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.15) - ); - props.setProperties( - "highway=motorway;cycleway=lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(2) - ); - props.setProperties( - "highway=motorway_link;cycleway=lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.15) - ); - - /* cycleway=share_busway */ - props.setProperties( - "highway=*;cycleway=share_busway", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.92) - ); - props.setProperties( - "highway=service;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.85) - ); - props.setProperties( - "highway=residential;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.85) - ); - props.setProperties( - "highway=residential_link;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.85) - ); - props.setProperties( - "highway=tertiary;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.92) - ); - props.setProperties( - "highway=tertiary_link;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.92) - ); - props.setProperties( - "highway=secondary;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.99) - ); - props.setProperties( - "highway=secondary_link;cycleway=share_busway", - withModes(ALL).bicycleSafety(0.99) - ); - props.setProperties( - "highway=primary;cycleway=share_busway", - withModes(ALL).bicycleSafety(1.25) - ); - props.setProperties( - "highway=primary_link;cycleway=share_busway", - withModes(ALL).bicycleSafety(1.25) - ); - props.setProperties( - "highway=trunk;cycleway=share_busway", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.75) - ); - props.setProperties( - "highway=trunk_link;cycleway=share_busway", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.25) - ); - props.setProperties( - "highway=motorway;cycleway=share_busway", - withModes(BICYCLE_AND_CAR).bicycleSafety(2.5) - ); - props.setProperties( - "highway=motorway_link;cycleway=share_busway", - withModes(BICYCLE_AND_CAR).bicycleSafety(1.25) - ); - - /* cycleway=opposite_lane */ - props.setProperties( - "highway=*;cycleway=opposite_lane", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1, 0.87) - ); - props.setProperties( - "highway=service;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(1.1, 0.77) - ); - props.setProperties( - "highway=residential;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(0.98, 0.77) - ); - props.setProperties( - "highway=residential_link;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(0.98, 0.77) - ); - props.setProperties( - "highway=tertiary;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(1, 0.87) - ); - props.setProperties( - "highway=tertiary_link;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(1, 0.87) - ); - props.setProperties( - "highway=secondary;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(1.5, 0.96) - ); - props.setProperties( - "highway=secondary_link;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(1.5, 0.96) - ); - props.setProperties( - "highway=primary;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(2.06, 1.15) - ); - props.setProperties( - "highway=primary_link;cycleway=opposite_lane", - withModes(ALL).bicycleSafety(2.06, 1.15) - ); - props.setProperties( - "highway=trunk;cycleway=opposite_lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(7.47, 1.5) - ); - props.setProperties( - "highway=trunk_link;cycleway=opposite_lane", - withModes(BICYCLE_AND_CAR).bicycleSafety(2.06, 1.15) - ); - - /* cycleway=track */ - props.setProperties( - "highway=*;cycleway=track", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) - ); - props.setProperties("highway=service;cycleway=track", withModes(ALL).bicycleSafety(0.65)); - props.setProperties("highway=residential;cycleway=track", withModes(ALL).bicycleSafety(0.65)); - props.setProperties( - "highway=residential_link;cycleway=track", - withModes(ALL).bicycleSafety(0.65) - ); - props.setProperties("highway=tertiary;cycleway=track", withModes(ALL).bicycleSafety(0.75)); - props.setProperties("highway=tertiary_link;cycleway=track", withModes(ALL).bicycleSafety(0.75)); - props.setProperties("highway=secondary;cycleway=track", withModes(ALL).bicycleSafety(0.8)); - props.setProperties("highway=secondary_link;cycleway=track", withModes(ALL).bicycleSafety(0.8)); - props.setProperties("highway=primary;cycleway=track", withModes(ALL).bicycleSafety(0.85)); - props.setProperties("highway=primary_link;cycleway=track", withModes(ALL).bicycleSafety(0.85)); - props.setProperties( - "highway=trunk;cycleway=track", - withModes(BICYCLE_AND_CAR).bicycleSafety(0.95) - ); - props.setProperties( - "highway=trunk_link;cycleway=track", - withModes(BICYCLE_AND_CAR).bicycleSafety(0.85) - ); - - /* cycleway=opposite_track */ - props.setProperties( - "highway=*;cycleway=opposite_track", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.0, 0.75) - ); - props.setProperties( - "highway=service;cycleway=opposite_track", - withModes(ALL).bicycleSafety(1.1, 0.65) - ); - props.setProperties( - "highway=residential;cycleway=opposite_track", - withModes(ALL).bicycleSafety(0.98, 0.65) - ); - props.setProperties( - "highway=residential_link;cycleway=opposite_track", - withModes(ALL).bicycleSafety(0.98, 0.65) - ); - props.setProperties( - "highway=tertiary;cycleway=opposite_track", - withModes(ALL).bicycleSafety(1, 0.75) - ); - props.setProperties( - "highway=tertiary_link;cycleway=opposite_track", - withModes(ALL).bicycleSafety(1, 0.75) - ); - props.setProperties( - "highway=secondary;cycleway=opposite_track", - withModes(ALL).bicycleSafety(1.5, 0.8) - ); - props.setProperties( - "highway=secondary_link;cycleway=opposite_track", - withModes(ALL).bicycleSafety(1.5, 0.8) - ); - props.setProperties( - "highway=primary;cycleway=opposite_track", - withModes(ALL).bicycleSafety(2.06, 0.85) - ); - props.setProperties( - "highway=primary_link;cycleway=opposite_track", - withModes(ALL).bicycleSafety(2.06, 0.85) - ); - props.setProperties( - "highway=trunk;cycleway=opposite_track", - withModes(BICYCLE_AND_CAR).bicycleSafety(7.47, 0.95) - ); - props.setProperties( - "highway=trunk_link;cycleway=opposite_track", - withModes(BICYCLE_AND_CAR).bicycleSafety(2.06, 0.85) - ); - - /* cycleway=shared_lane a.k.a. bike boulevards or neighborhood greenways */ - props.setProperties( - "highway=*;cycleway=shared_lane", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.77) - ); - props.setProperties("highway=service;cycleway=shared_lane", withModes(ALL).bicycleSafety(0.73)); - props.setProperties( - "highway=residential;cycleway=shared_lane", - withModes(ALL).bicycleSafety(0.77) - ); - props.setProperties( - "highway=residential_link;cycleway=shared_lane", - withModes(ALL).bicycleSafety(0.77) - ); - props.setProperties( - "highway=tertiary;cycleway=shared_lane", - withModes(ALL).bicycleSafety(0.83) - ); - props.setProperties( - "highway=tertiary_link;cycleway=shared_lane", - withModes(ALL).bicycleSafety(0.83) - ); - props.setProperties( - "highway=secondary;cycleway=shared_lane", - withModes(ALL).bicycleSafety(1.25) - ); - props.setProperties( - "highway=secondary_link;cycleway=shared_lane", - withModes(ALL).bicycleSafety(1.25) - ); - props.setProperties("highway=primary;cycleway=shared_lane", withModes(ALL).bicycleSafety(1.75)); - props.setProperties( - "highway=primary_link;cycleway=shared_lane", - withModes(ALL).bicycleSafety(1.75) - ); - - /* cycleway=opposite */ - props.setProperties( - "highway=*;cycleway=opposite", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1, 1.4) - ); - props.setProperties("highway=service;cycleway=opposite", withModes(ALL).bicycleSafety(1.1)); - props.setProperties( - "highway=residential;cycleway=opposite", - withModes(ALL).bicycleSafety(0.98) - ); - props.setProperties( - "highway=residential_link;cycleway=opposite", - withModes(ALL).bicycleSafety(0.98) - ); - props.setProperties("highway=tertiary;cycleway=opposite", allWayProperties); - props.setProperties("highway=tertiary_link;cycleway=opposite", allWayProperties); - props.setProperties( - "highway=secondary;cycleway=opposite", - withModes(ALL).bicycleSafety(1.5, 1.71) - ); - props.setProperties( - "highway=secondary_link;cycleway=opposite", - withModes(ALL).bicycleSafety(1.5, 1.71) - ); - props.setProperties( - "highway=primary;cycleway=opposite", - withModes(ALL).bicycleSafety(2.06, 2.99) - ); - props.setProperties( - "highway=primary_link;cycleway=opposite", - withModes(ALL).bicycleSafety(2.06, 2.99) - ); - - /* - * path designed for bicycles (should be treated exactly as a cycleway is), this is a multi-use path (MUP) - */ - props.setProperties( - "highway=path;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.60) - ); - - /* special cases for footway, pedestrian and bicycles */ - props.setProperties( - "highway=footway;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) - ); - props.setProperties( - "highway=footway;bicycle=yes;area=yes", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.9) - ); - props.setProperties( - "highway=pedestrian;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) - ); - - /* sidewalk and crosswalk */ - props.setProperties( - "footway=sidewalk;highway=footway;bicycle=yes", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(2.5) - ); - props.setProperties( - "footway=sidewalk;highway=footway;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1) - ); - props.setProperties( - "highway=footway;footway=crossing", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(2.5) - ); - props.setProperties( - "highway=footway;footway=crossing;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1) - ); - - /* - * bicycles on tracks (tracks are defined in OSM as: Roads for agricultural use, gravel roads in the forest etc.; usually unpaved/unsealed but - * may occasionally apply to paved tracks as well.) - */ - props.setProperties( - "highway=track;bicycle=yes", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.18) - ); - props.setProperties( - "highway=track;bicycle=designated", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.99) - ); - props.setProperties( - "highway=track;bicycle=yes;surface=*", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.18) - ); - props.setProperties( - "highway=track;bicycle=designated;surface=*", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.99) - ); - /* this is to avoid double counting since tracks are almost of surface type that is penalized */ - props.setProperties( - "highway=track;surface=*", - withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.3) - ); - - /* bicycle=designated, but no bike infrastructure is present */ - props.setProperties("highway=*;bicycle=designated", withModes(ALL).bicycleSafety(0.97)); - props.setProperties("highway=service;bicycle=designated", withModes(ALL).bicycleSafety(0.84)); - props.setProperties( - "highway=residential;bicycle=designated", - withModes(ALL).bicycleSafety(0.95) - ); - props.setProperties( - "highway=unclassified;bicycle=designated", - withModes(ALL).bicycleSafety(0.95) - ); - props.setProperties( - "highway=residential_link;bicycle=designated", - withModes(ALL).bicycleSafety(0.95) - ); - props.setProperties("highway=tertiary;bicycle=designated", withModes(ALL).bicycleSafety(0.97)); - props.setProperties( - "highway=tertiary_link;bicycle=designated", - withModes(ALL).bicycleSafety(0.97) - ); - props.setProperties("highway=secondary;bicycle=designated", withModes(ALL).bicycleSafety(1.46)); - props.setProperties( - "highway=secondary_link;bicycle=designated", - withModes(ALL).bicycleSafety(1.46) - ); - props.setProperties("highway=primary;bicycle=designated", withModes(ALL).bicycleSafety(2)); - props.setProperties("highway=primary_link;bicycle=designated", withModes(ALL).bicycleSafety(2)); - props.setProperties( - "highway=trunk;bicycle=designated", - withModes(BICYCLE_AND_CAR).bicycleSafety(7.25) - ); - props.setProperties( - "highway=trunk_link;bicycle=designated", - withModes(BICYCLE_AND_CAR).bicycleSafety(2) - ); - props.setProperties( - "highway=motorway;bicycle=designated", - withModes(BICYCLE_AND_CAR).bicycleSafety(7.76) - ); - props.setProperties( - "highway=motorway_link;bicycle=designated", - withModes(BICYCLE_AND_CAR).bicycleSafety(2) - ); - - // We assume highway/cycleway of a cycle network to be safer (for bicycle network relations, their network is copied to way in postLoad) - // this uses a OR since you don't want to apply the safety multiplier more than once. - // Signed bicycle_roads and cyclestreets exist in traffic codes of some european countries. - // Tagging in OSM and on-the-ground use is varied, so just assume they are "somehow safer", too. - // In my test area ways often, but not always, have both tags. - // For simplicity these two concepts are handled together. - props.setMixinProperties( - new LogicalOrSpecifier( - "lcn=yes", - "rcn=yes", - "ncn=yes", - "bicycle_road=yes", - "cyclestreet=yes" - ), - ofBicycleSafety(0.7) - ); - - /* - * Automobile speeds in the United States: Based on my (mattwigway) personal experience, primarily in California - */ - props.setCarSpeed("highway=motorway", 29); // 29 m/s ~= 65 mph - props.setCarSpeed("highway=motorway_link", 15); // ~= 35 mph - props.setCarSpeed("highway=trunk", 24.6f); // ~= 55 mph - props.setCarSpeed("highway=trunk_link", 15); // ~= 35 mph - props.setCarSpeed("highway=primary", 20); // ~= 45 mph - props.setCarSpeed("highway=primary_link", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=secondary", 15); // ~= 35 mph - props.setCarSpeed("highway=secondary_link", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=tertiary", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=tertiary_link", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=living_street", 2.2f); // ~= 5 mph - - // generally, these will not allow cars at all, but the docs say - // "For roads used mainly/exclusively for pedestrians . . . which may allow access by - // motorised vehicles only for very limited periods of the day." - // http://wiki.openstreetmap.org/wiki/Key:highway - // This of course makes the street network time-dependent - props.setCarSpeed("highway=pedestrian", 2.2f); // ~= 5 mph - - props.setCarSpeed("highway=residential", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=unclassified", 11.2f); // ~= 25 mph - props.setCarSpeed("highway=service", 6.7f); // ~= 15 mph - props.setCarSpeed("highway=track", 4.5f); // ~= 10 mph - props.setCarSpeed("highway=road", 11.2f); // ~= 25 mph - - // default ~= 25 mph - props.defaultCarSpeed = 11.2f; - // 38 m/s ~= 85 mph ~= 137 kph - props.maxPossibleCarSpeed = 38f; - - /* special situations */ - - /* - * cycleway:left/right=lane/track/shared_lane permutations - no longer needed because left/right matching algorithm does this - */ - - /* cycleway:left=lane */ - /* cycleway:right=track */ - /* cycleway:left=track */ - /* cycleway:right=shared_lane */ - /* cycleway:left=shared_lane */ - /* cycleway:right=lane, cycleway:left=track */ - /* cycleway:right=lane, cycleway:left=shared_lane */ - /* cycleway:right=track, cycleway:left=lane */ - /* cycleway:right=track, cycleway:left=shared_lane */ - /* cycleway:right=shared_lane, cycleway:left=lane */ - /* cycleway:right=shared_lane, cycleway:left=track */ - - /* surface=* mixins */ - - /* - * The following tags have been removed from surface weights because they are no more of an impedence to bicycling than a paved surface - * surface=paving_stones surface=fine_gravel (sounds counter-intuitive but see the definition on the OSM Wiki) surface=tartan (this what - * running tracks are usually made of) - */ - - props.setMixinProperties("surface=unpaved", ofBicycleSafety(1.18)); - props.setMixinProperties("surface=compacted", ofBicycleSafety(1.18)); - props.setMixinProperties("surface=wood", ofBicycleSafety(1.18)); - - props.setMixinProperties("surface=cobblestone", ofBicycleSafety(1.3)); - props.setMixinProperties("surface=sett", ofBicycleSafety(1.3)); - props.setMixinProperties("surface=unhewn_cobblestone", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=grass_paver", ofBicycleSafety(1.3)); - props.setMixinProperties("surface=pebblestone", ofBicycleSafety(1.3)); - // Can be slick if wet, but otherwise not unfavorable to bikes - props.setMixinProperties("surface=metal", ofBicycleSafety(1.3)); - props.setMixinProperties("surface=ground", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=dirt", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=earth", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=grass", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=mud", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=woodchip", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=gravel", ofBicycleSafety(1.5)); - props.setMixinProperties("surface=artifical_turf", ofBicycleSafety(1.5)); - - /* sand is deadly for bikes */ - props.setMixinProperties("surface=sand", ofBicycleSafety(100)); - - /* Portland-local mixins */ - - props.setMixinProperties("foot=discouraged", ofWalkSafety(3)); - props.setMixinProperties("bicycle=discouraged", ofBicycleSafety(3)); - - props.setMixinProperties("foot=use_sidepath", ofWalkSafety(5)); - props.setMixinProperties("bicycle=use_sidepath", ofBicycleSafety(5)); - - populateNotesAndNames(props); - - // slope overrides - props.setSlopeOverride(new BestMatchSpecifier("bridge=*"), true); - props.setSlopeOverride(new BestMatchSpecifier("embankment=*"), true); - props.setSlopeOverride(new BestMatchSpecifier("cutting=*"), true); - props.setSlopeOverride(new BestMatchSpecifier("tunnel=*"), true); - props.setSlopeOverride(new BestMatchSpecifier("location=underground"), true); - props.setSlopeOverride(new BestMatchSpecifier("indoor=yes"), true); - } - - public void populateNotesAndNames(WayPropertySet props) { - /* and the notes */ - // TODO: The curly brackets in the string below mean that the CreativeNamer should substitute in OSM tag values. - // However they are not taken into account when passed to the translation function. - // props.createNotes("wheelchair:description=*", "{wheelchair:description}", StreetNotesService.WHEELCHAIR_MATCHER); - // TODO: The two entries below produce lots of spurious notes (because of OSM mapper comments) - // props.createNotes("note=*", "{note}", StreetNotesService.ALWAYS_MATCHER); - // props.createNotes("notes=*", "{notes}", StreetNotesService.ALWAYS_MATCHER); - props.createNotes( - "RLIS:bicycle=caution_area", - "note.caution", - StreetNotesService.BICYCLE_MATCHER - ); - props.createNotes( - "CCGIS:bicycle=caution_area", - "note.caution", - StreetNotesService.BICYCLE_MATCHER - ); - // TODO: Maybe we should apply the following notes only for car/bike - props.createNotes("surface=unpaved", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes( - "surface=compacted", - "note.unpaved_surface", - StreetNotesService.ALWAYS_MATCHER - ); - props.createNotes("surface=ground", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes("surface=dirt", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes("surface=earth", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes("surface=grass", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes("surface=mud", "note.muddy_surface", StreetNotesService.ALWAYS_MATCHER); - props.createNotes("toll=yes", "note.toll", StreetNotesService.DRIVING_MATCHER); - props.createNotes("toll:motorcar=yes", "note.toll", StreetNotesService.DRIVING_MATCHER); - - /* and some names */ - // Basics - props.createNames("highway=cycleway", "name.bike_path"); - props.createNames("cycleway=track", "name.bike_path"); - props.createNames("highway=pedestrian", "name.pedestrian_path"); - props.createNames("highway=pedestrian;area=yes", "name.pedestrian_area"); - props.createNames("highway=path", "name.path"); - props.createNames("highway=footway", "name.pedestrian_path"); - props.createNames("highway=bridleway", "name.bridleway"); - props.createNames("highway=footway;bicycle=no", "name.pedestrian_path"); - - // Platforms - props.createNames("otp:route_ref=*", "name.otp_route_ref"); - props.createNames("highway=platform;ref=*", "name.platform_ref"); - props.createNames("railway=platform;ref=*", "name.platform_ref"); - props.createNames("railway=platform;highway=footway;footway=sidewalk", "name.platform"); - props.createNames("railway=platform;highway=path;path=sidewalk", "name.platform"); - props.createNames("railway=platform;highway=pedestrian", "name.platform"); - props.createNames("railway=platform;highway=path", "name.platform"); - props.createNames("railway=platform;highway=footway", "name.platform"); - props.createNames("highway=platform", "name.platform"); - props.createNames("railway=platform", "name.platform"); - props.createNames("railway=platform;highway=footway;bicycle=no", "name.platform"); - - // Bridges/Tunnels - props.createNames("highway=pedestrian;bridge=*", "name.footbridge"); - props.createNames("highway=path;bridge=*", "name.footbridge"); - props.createNames("highway=footway;bridge=*", "name.footbridge"); - - props.createNames("highway=pedestrian;tunnel=*", "name.underpass"); - props.createNames("highway=path;tunnel=*", "name.underpass"); - props.createNames("highway=footway;tunnel=*", "name.underpass"); - - // Basic Mappings - props.createNames("highway=motorway", "name.road"); - props.createNames("highway=motorway_link", "name.ramp"); - props.createNames("highway=trunk", "name.road"); - props.createNames("highway=trunk_link", "name.ramp"); - - props.createNames("highway=primary", "name.road"); - props.createNames("highway=primary_link", "name.link"); - props.createNames("highway=secondary", "name.road"); - props.createNames("highway=secondary_link", "name.link"); - props.createNames("highway=tertiary", "name.road"); - props.createNames("highway=tertiary_link", "name.link"); - props.createNames("highway=unclassified", "name.road"); - props.createNames("highway=residential", "name.road"); - props.createNames("highway=living_street", "name.road"); - props.createNames("highway=road", "name.road"); - props.createNames("highway=service", "name.service_road"); - props.createNames("highway=service;service=alley", "name.alley"); - props.createNames("highway=service;service=parking_aisle", "name.parking_aisle"); - props.createNames("highway=byway", "name.byway"); - props.createNames("highway=track", "name.track"); - - props.createNames("highway=footway;footway=sidewalk", "name.sidewalk"); - props.createNames("highway=path;path=sidewalk", "name.sidewalk"); - - props.createNames("highway=steps", "name.steps"); - - props.createNames("amenity=bicycle_parking;name=*", "name.bicycle_parking_name"); - props.createNames("amenity=bicycle_parking", "name.bicycle_parking"); - - props.createNames("amenity=parking;name=*", "name.park_and_ride_name"); - props.createNames("amenity=parking", "name.park_and_ride_station"); - } -} diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/FinlandMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/FinlandMapper.java index e796ebff17f..e9ac9478552 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/FinlandMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/FinlandMapper.java @@ -8,6 +8,7 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; +import java.util.Set; import org.opentripplanner.framework.functional.FunctionUtils.TriFunction; import org.opentripplanner.osm.model.OsmWithTags; import org.opentripplanner.osm.wayproperty.WayPropertySet; @@ -23,9 +24,16 @@ * * @author juusokor * @see OsmTagMapper - * @see DefaultMapper */ -class FinlandMapper implements OsmTagMapper { +class FinlandMapper extends OsmTagMapper { + + private static final Set NOTHROUGH_DRIVING_TAGS = Set.of( + "parking_aisle", + "driveway", + "alley", + "emergency_access", + "drive-through" + ); @Override public void populateProperties(WayPropertySet props) { @@ -203,12 +211,11 @@ else if (speedLimit <= 16.65f) { // ~= 16 kph props.setCarSpeed("highway=track", 4.5f); - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } @Override - public boolean isBicycleNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isBicycleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String bicycle = way.getTag("bicycle"); return ( isVehicleThroughTrafficExplicitlyDisallowed(way) || @@ -217,8 +224,16 @@ public boolean isBicycleNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { } @Override - public boolean isWalkNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isWalkThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String foot = way.getTag("foot"); return isGeneralNoThroughTraffic(way) || doesTagValueDisallowThroughTraffic(foot); } + + @Override + public boolean isMotorVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + if (super.isMotorVehicleThroughTrafficExplicitlyDisallowed(way)) { + return true; + } + return way.isOneOfTags("service", NOTHROUGH_DRIVING_TAGS); + } } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/GermanyMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/GermanyMapper.java index 70a5bd593aa..af56b572bd8 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/GermanyMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/GermanyMapper.java @@ -15,9 +15,8 @@ * networks. * * @see OsmTagMapper - * @see DefaultMapper */ -class GermanyMapper implements OsmTagMapper { +class GermanyMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -88,7 +87,6 @@ public void populateProperties(WayPropertySet props) { props.setProperties("highway=unclassified;cycleway=lane", withModes(ALL).bicycleSafety(0.87)); - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/HamburgMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/HamburgMapper.java index 47bd5164d1f..755f5864ba2 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/HamburgMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/HamburgMapper.java @@ -7,7 +7,6 @@ * * @see GermanyMapper * @see OsmTagMapper - * @see DefaultMapper * * @author Maintained by HBT (geofox-team@hbt.de) */ diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/HoustonMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/HoustonMapper.java index 7e4aba9da4e..1d24dbafffb 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/HoustonMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/HoustonMapper.java @@ -14,7 +14,7 @@ * 1. In Houston we want to disallow usage of downtown pedestrian tunnel system. */ -class HoustonMapper implements OsmTagMapper { +class HoustonMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -26,11 +26,9 @@ public void populateProperties(WayPropertySet props) { new ExactMatchSpecifier("highway=footway;layer=-1;tunnel=yes;indoor=yes"), withModes(NONE) ); - // Max speed limit in Texas is 38 m/s ~= 85 mph ~= 137 kph props.maxPossibleCarSpeed = 38f; - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/NorwayMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/NorwayMapper.java index 9e06c0aa591..c37de8533c6 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/NorwayMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/NorwayMapper.java @@ -24,9 +24,9 @@ * * @author seime * @see OsmTagMapper - * @see DefaultMapper + * @see OsmTagMapper */ -class NorwayMapper implements OsmTagMapper { +class NorwayMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -621,7 +621,7 @@ else if (speedLimit >= 11.1f) { props.defaultCarSpeed = 22.22f; // 80 km/h props.maxPossibleCarSpeed = 30.56f; // 110 km/h - new DefaultMapper().populateNotesAndNames(props); + super.populateNotesAndNames(props); props.setSlopeOverride(new BestMatchSpecifier("bridge=*"), true); props.setSlopeOverride(new BestMatchSpecifier("cutting=*"), true); diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java index 9bf0ed2d20d..2df3c22d6a9 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java @@ -1,18 +1,727 @@ package org.opentripplanner.osm.tagmapping; +import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofBicycleSafety; +import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofWalkSafety; +import static org.opentripplanner.osm.wayproperty.WayPropertiesBuilder.withModes; +import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; +import static org.opentripplanner.street.model.StreetTraversalPermission.BICYCLE_AND_CAR; +import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; +import static org.opentripplanner.street.model.StreetTraversalPermission.NONE; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; + import org.opentripplanner.osm.model.OsmWithTags; +import org.opentripplanner.osm.wayproperty.WayProperties; import org.opentripplanner.osm.wayproperty.WayPropertySet; +import org.opentripplanner.osm.wayproperty.specifier.BestMatchSpecifier; +import org.opentripplanner.osm.wayproperty.specifier.Condition; +import org.opentripplanner.osm.wayproperty.specifier.ExactMatchSpecifier; +import org.opentripplanner.osm.wayproperty.specifier.LogicalOrSpecifier; +import org.opentripplanner.routing.services.notes.StreetNotesService; /** - * Interface for populating a {@link WayPropertySet} that determine how OSM streets can be traversed - * in various modes and named. + * This factory class provides a default collection of {@link WayProperties} that determine how OSM + * streets can be traversed in various modes. + *

+ * Circa January 2011, Grant and Mele at TriMet undertook proper testing of bike (and transit) + * routing, and worked with David Turner on assigning proper weights to different facility types. + * The weights in this file grew organically from trial and error, and are the result of months of + * testing and tweaking the routes that OTP returned, as well as actually walking/biking these + * routes and making changes based on those experiences. This set of weights should be a great + * starting point for others to use, but they are to some extent tailored to the situation in + * Portland and people shouldn't hesitate to adjust them to for their own instance. + *

+ * The rules for assigning WayProperties to OSM ways are explained in. The final tie breaker if two + * Pickers both match is the sequence that the properties are added in this file: if all else is + * equal the 'props.setProperties' statement that is closer to the top of the page will prevail over + * those lower down the page. + *

+ * Foot and bicycle permissions are also addressed in OpenStreetMapGraphBuilderImpl.Handler#getPermissionsForEntity(). + * For instance, if a way that normally does not permit walking based on its tag matches (the + * prevailing 'props.setProperties' statement) has a 'foot=yes' tag the permissions are overridden + * and walking is allowed on that way. + *

* - * @author bdferris, novalis, seime + * @author bdferris, novalis */ -public interface OsmTagMapper { - void populateProperties(WayPropertySet wayPropertySet); - default boolean doesTagValueDisallowThroughTraffic(String tagValue) { +public class OsmTagMapper { + + /* Populate properties on existing WayPropertySet */ + public void populateProperties(WayPropertySet props) { + WayProperties allWayProperties = withModes(ALL).build(); + WayProperties noneWayProperties = withModes(NONE).build(); + WayProperties pedestrianWayProperties = withModes(PEDESTRIAN).build(); + WayProperties pedestrianAndBicycleWayProperties = withModes(PEDESTRIAN_AND_BICYCLE).build(); + /* no bicycle tags */ + + /* NONE */ + props.setProperties("mtb:scale=3", noneWayProperties); + props.setProperties("mtb:scale=4", noneWayProperties); + props.setProperties("mtb:scale=5", noneWayProperties); + props.setProperties("mtb:scale=6", noneWayProperties); + + /* PEDESTRIAN */ + props.setProperties("highway=corridor", pedestrianWayProperties); + props.setProperties("highway=steps", pedestrianWayProperties); + props.setProperties("highway=crossing", pedestrianWayProperties); + props.setProperties("highway=platform", pedestrianWayProperties); + props.setProperties("public_transport=platform", pedestrianWayProperties); + props.setProperties("railway=platform", pedestrianWayProperties); + props.setProperties("footway=sidewalk;highway=footway", pedestrianWayProperties); + props.setProperties("mtb:scale=1", pedestrianWayProperties); + props.setProperties("mtb:scale=2", pedestrianWayProperties); + + /* PEDESTRIAN_AND_BICYCLE */ + props.setProperties("mtb:scale=0", pedestrianAndBicycleWayProperties); + props.setProperties("highway=cycleway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.6)); + props.setProperties("highway=path", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75)); + props.setProperties("highway=pedestrian", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.9)); + props.setProperties("highway=footway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1)); + props.setProperties("highway=bridleway", withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.3)); + + /* ALL */ + props.setProperties("highway=living_street", withModes(ALL).bicycleSafety(0.9)); + props.setProperties("highway=unclassified", allWayProperties); + props.setProperties("highway=road", allWayProperties); + props.setProperties("highway=byway", withModes(ALL).bicycleSafety(1.3)); + props.setProperties("highway=track", withModes(ALL).bicycleSafety(1.3)); + props.setProperties("highway=service", withModes(ALL).bicycleSafety(1.1)); + props.setProperties("highway=residential", withModes(ALL).bicycleSafety(0.98)); + props.setProperties("highway=residential_link", withModes(ALL).bicycleSafety(0.98)); + props.setProperties("highway=tertiary", allWayProperties); + props.setProperties("highway=tertiary_link", allWayProperties); + props.setProperties("highway=secondary", withModes(ALL).bicycleSafety(1.5)); + props.setProperties("highway=secondary_link", withModes(ALL).bicycleSafety(1.5)); + props.setProperties("highway=primary", withModes(ALL).bicycleSafety(2.06)); + props.setProperties("highway=primary_link", withModes(ALL).bicycleSafety(2.06)); + + /* DRIVING ONLY */ + // trunk and motorway links are often short distances and necessary connections + props.setProperties("highway=trunk_link", withModes(CAR).bicycleSafety(2.06)); + props.setProperties("highway=motorway_link", withModes(CAR).bicycleSafety(2.06)); + + props.setProperties("highway=trunk", withModes(CAR).bicycleSafety(7.47)); + props.setProperties("highway=motorway", withModes(CAR).bicycleSafety(8)); + + /* cycleway=lane */ + props.setProperties( + "highway=*;cycleway=lane", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.87) + ); + props.setProperties("highway=service;cycleway=lane", withModes(ALL).bicycleSafety(0.77)); + props.setProperties("highway=residential;cycleway=lane", withModes(ALL).bicycleSafety(0.77)); + props.setProperties( + "highway=residential_link;cycleway=lane", + withModes(ALL).bicycleSafety(0.77) + ); + props.setProperties("highway=tertiary;cycleway=lane", withModes(ALL).bicycleSafety(0.87)); + props.setProperties("highway=tertiary_link;cycleway=lane", withModes(ALL).bicycleSafety(0.87)); + props.setProperties("highway=secondary;cycleway=lane", withModes(ALL).bicycleSafety(0.96)); + props.setProperties("highway=secondary_link;cycleway=lane", withModes(ALL).bicycleSafety(0.96)); + props.setProperties("highway=primary;cycleway=lane", withModes(ALL).bicycleSafety(1.15)); + props.setProperties("highway=primary_link;cycleway=lane", withModes(ALL).bicycleSafety(1.15)); + + /* BICYCLE_AND_CAR */ + props.setProperties( + "highway=trunk;cycleway=lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.5) + ); + props.setProperties( + "highway=trunk_link;cycleway=lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.15) + ); + props.setProperties( + "highway=motorway;cycleway=lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(2) + ); + props.setProperties( + "highway=motorway_link;cycleway=lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.15) + ); + + /* cycleway=share_busway */ + props.setProperties( + "highway=*;cycleway=share_busway", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.92) + ); + props.setProperties( + "highway=service;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.85) + ); + props.setProperties( + "highway=residential;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.85) + ); + props.setProperties( + "highway=residential_link;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.85) + ); + props.setProperties( + "highway=tertiary;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.92) + ); + props.setProperties( + "highway=tertiary_link;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.92) + ); + props.setProperties( + "highway=secondary;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.99) + ); + props.setProperties( + "highway=secondary_link;cycleway=share_busway", + withModes(ALL).bicycleSafety(0.99) + ); + props.setProperties( + "highway=primary;cycleway=share_busway", + withModes(ALL).bicycleSafety(1.25) + ); + props.setProperties( + "highway=primary_link;cycleway=share_busway", + withModes(ALL).bicycleSafety(1.25) + ); + props.setProperties( + "highway=trunk;cycleway=share_busway", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.75) + ); + props.setProperties( + "highway=trunk_link;cycleway=share_busway", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.25) + ); + props.setProperties( + "highway=motorway;cycleway=share_busway", + withModes(BICYCLE_AND_CAR).bicycleSafety(2.5) + ); + props.setProperties( + "highway=motorway_link;cycleway=share_busway", + withModes(BICYCLE_AND_CAR).bicycleSafety(1.25) + ); + + /* cycleway=opposite_lane */ + props.setProperties( + "highway=*;cycleway=opposite_lane", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1, 0.87) + ); + props.setProperties( + "highway=service;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(1.1, 0.77) + ); + props.setProperties( + "highway=residential;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(0.98, 0.77) + ); + props.setProperties( + "highway=residential_link;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(0.98, 0.77) + ); + props.setProperties( + "highway=tertiary;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(1, 0.87) + ); + props.setProperties( + "highway=tertiary_link;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(1, 0.87) + ); + props.setProperties( + "highway=secondary;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(1.5, 0.96) + ); + props.setProperties( + "highway=secondary_link;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(1.5, 0.96) + ); + props.setProperties( + "highway=primary;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(2.06, 1.15) + ); + props.setProperties( + "highway=primary_link;cycleway=opposite_lane", + withModes(ALL).bicycleSafety(2.06, 1.15) + ); + props.setProperties( + "highway=trunk;cycleway=opposite_lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(7.47, 1.5) + ); + props.setProperties( + "highway=trunk_link;cycleway=opposite_lane", + withModes(BICYCLE_AND_CAR).bicycleSafety(2.06, 1.15) + ); + + /* cycleway=track */ + props.setProperties( + "highway=*;cycleway=track", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) + ); + props.setProperties("highway=service;cycleway=track", withModes(ALL).bicycleSafety(0.65)); + props.setProperties("highway=residential;cycleway=track", withModes(ALL).bicycleSafety(0.65)); + props.setProperties( + "highway=residential_link;cycleway=track", + withModes(ALL).bicycleSafety(0.65) + ); + props.setProperties("highway=tertiary;cycleway=track", withModes(ALL).bicycleSafety(0.75)); + props.setProperties("highway=tertiary_link;cycleway=track", withModes(ALL).bicycleSafety(0.75)); + props.setProperties("highway=secondary;cycleway=track", withModes(ALL).bicycleSafety(0.8)); + props.setProperties("highway=secondary_link;cycleway=track", withModes(ALL).bicycleSafety(0.8)); + props.setProperties("highway=primary;cycleway=track", withModes(ALL).bicycleSafety(0.85)); + props.setProperties("highway=primary_link;cycleway=track", withModes(ALL).bicycleSafety(0.85)); + props.setProperties( + "highway=trunk;cycleway=track", + withModes(BICYCLE_AND_CAR).bicycleSafety(0.95) + ); + props.setProperties( + "highway=trunk_link;cycleway=track", + withModes(BICYCLE_AND_CAR).bicycleSafety(0.85) + ); + + /* cycleway=opposite_track */ + props.setProperties( + "highway=*;cycleway=opposite_track", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.0, 0.75) + ); + props.setProperties( + "highway=service;cycleway=opposite_track", + withModes(ALL).bicycleSafety(1.1, 0.65) + ); + props.setProperties( + "highway=residential;cycleway=opposite_track", + withModes(ALL).bicycleSafety(0.98, 0.65) + ); + props.setProperties( + "highway=residential_link;cycleway=opposite_track", + withModes(ALL).bicycleSafety(0.98, 0.65) + ); + props.setProperties( + "highway=tertiary;cycleway=opposite_track", + withModes(ALL).bicycleSafety(1, 0.75) + ); + props.setProperties( + "highway=tertiary_link;cycleway=opposite_track", + withModes(ALL).bicycleSafety(1, 0.75) + ); + props.setProperties( + "highway=secondary;cycleway=opposite_track", + withModes(ALL).bicycleSafety(1.5, 0.8) + ); + props.setProperties( + "highway=secondary_link;cycleway=opposite_track", + withModes(ALL).bicycleSafety(1.5, 0.8) + ); + props.setProperties( + "highway=primary;cycleway=opposite_track", + withModes(ALL).bicycleSafety(2.06, 0.85) + ); + props.setProperties( + "highway=primary_link;cycleway=opposite_track", + withModes(ALL).bicycleSafety(2.06, 0.85) + ); + props.setProperties( + "highway=trunk;cycleway=opposite_track", + withModes(BICYCLE_AND_CAR).bicycleSafety(7.47, 0.95) + ); + props.setProperties( + "highway=trunk_link;cycleway=opposite_track", + withModes(BICYCLE_AND_CAR).bicycleSafety(2.06, 0.85) + ); + + /* cycleway=shared_lane a.k.a. bike boulevards or neighborhood greenways */ + props.setProperties( + "highway=*;cycleway=shared_lane", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.77) + ); + props.setProperties("highway=service;cycleway=shared_lane", withModes(ALL).bicycleSafety(0.73)); + props.setProperties( + "highway=residential;cycleway=shared_lane", + withModes(ALL).bicycleSafety(0.77) + ); + props.setProperties( + "highway=residential_link;cycleway=shared_lane", + withModes(ALL).bicycleSafety(0.77) + ); + props.setProperties( + "highway=tertiary;cycleway=shared_lane", + withModes(ALL).bicycleSafety(0.83) + ); + props.setProperties( + "highway=tertiary_link;cycleway=shared_lane", + withModes(ALL).bicycleSafety(0.83) + ); + props.setProperties( + "highway=secondary;cycleway=shared_lane", + withModes(ALL).bicycleSafety(1.25) + ); + props.setProperties( + "highway=secondary_link;cycleway=shared_lane", + withModes(ALL).bicycleSafety(1.25) + ); + props.setProperties("highway=primary;cycleway=shared_lane", withModes(ALL).bicycleSafety(1.75)); + props.setProperties( + "highway=primary_link;cycleway=shared_lane", + withModes(ALL).bicycleSafety(1.75) + ); + + /* cycleway=opposite */ + props.setProperties( + "highway=*;cycleway=opposite", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1, 1.4) + ); + props.setProperties("highway=service;cycleway=opposite", withModes(ALL).bicycleSafety(1.1)); + props.setProperties( + "highway=residential;cycleway=opposite", + withModes(ALL).bicycleSafety(0.98) + ); + props.setProperties( + "highway=residential_link;cycleway=opposite", + withModes(ALL).bicycleSafety(0.98) + ); + props.setProperties("highway=tertiary;cycleway=opposite", allWayProperties); + props.setProperties("highway=tertiary_link;cycleway=opposite", allWayProperties); + props.setProperties( + "highway=secondary;cycleway=opposite", + withModes(ALL).bicycleSafety(1.5, 1.71) + ); + props.setProperties( + "highway=secondary_link;cycleway=opposite", + withModes(ALL).bicycleSafety(1.5, 1.71) + ); + props.setProperties( + "highway=primary;cycleway=opposite", + withModes(ALL).bicycleSafety(2.06, 2.99) + ); + props.setProperties( + "highway=primary_link;cycleway=opposite", + withModes(ALL).bicycleSafety(2.06, 2.99) + ); + + /* + * path designed for bicycles (should be treated exactly as a cycleway is), this is a multi-use path (MUP) + */ + props.setProperties( + "highway=path;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.60) + ); + + /* special cases for footway, pedestrian and bicycles */ + props.setProperties( + "highway=footway;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) + ); + props.setProperties( + new ExactMatchSpecifier("highway=footway;bicycle=yes;area=yes"), + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.9) + ); + props.setProperties( + "highway=pedestrian;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.75) + ); + + /* sidewalk and crosswalk */ + props.setProperties( + "footway=sidewalk;highway=footway;bicycle=yes", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(2.5) + ); + props.setProperties( + "footway=sidewalk;highway=footway;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1) + ); + props.setProperties( + "highway=footway;footway=crossing", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(2.5) + ); + props.setProperties( + "highway=footway;footway=crossing;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.1) + ); + + /* + * bicycles on tracks (tracks are defined in OSM as: Roads for agricultural use, gravel roads in the forest etc.; usually unpaved/unsealed but + * may occasionally apply to paved tracks as well.) + */ + props.setProperties( + "highway=track;bicycle=yes", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.18) + ); + props.setProperties( + "highway=track;bicycle=designated", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.99) + ); + props.setProperties( + "highway=track;bicycle=yes;surface=*", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.18) + ); + props.setProperties( + "highway=track;bicycle=designated;surface=*", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.99) + ); + /* this is to avoid double counting since tracks are almost of surface type that is penalized */ + props.setProperties( + "highway=track;surface=*", + withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.3) + ); + + /* bicycle=designated, but no bike infrastructure is present */ + props.setProperties("highway=*;bicycle=designated", withModes(ALL).bicycleSafety(0.97)); + props.setProperties("highway=service;bicycle=designated", withModes(ALL).bicycleSafety(0.84)); + props.setProperties( + "highway=residential;bicycle=designated", + withModes(ALL).bicycleSafety(0.95) + ); + props.setProperties( + "highway=unclassified;bicycle=designated", + withModes(ALL).bicycleSafety(0.95) + ); + props.setProperties( + "highway=residential_link;bicycle=designated", + withModes(ALL).bicycleSafety(0.95) + ); + props.setProperties("highway=tertiary;bicycle=designated", withModes(ALL).bicycleSafety(0.97)); + props.setProperties( + "highway=tertiary_link;bicycle=designated", + withModes(ALL).bicycleSafety(0.97) + ); + props.setProperties("highway=secondary;bicycle=designated", withModes(ALL).bicycleSafety(1.46)); + props.setProperties( + "highway=secondary_link;bicycle=designated", + withModes(ALL).bicycleSafety(1.46) + ); + props.setProperties("highway=primary;bicycle=designated", withModes(ALL).bicycleSafety(2)); + props.setProperties("highway=primary_link;bicycle=designated", withModes(ALL).bicycleSafety(2)); + props.setProperties( + "highway=trunk;bicycle=designated", + withModes(BICYCLE_AND_CAR).bicycleSafety(7.25) + ); + props.setProperties( + "highway=trunk_link;bicycle=designated", + withModes(BICYCLE_AND_CAR).bicycleSafety(2) + ); + props.setProperties( + "highway=motorway;bicycle=designated", + withModes(BICYCLE_AND_CAR).bicycleSafety(7.76) + ); + props.setProperties( + "highway=motorway_link;bicycle=designated", + withModes(BICYCLE_AND_CAR).bicycleSafety(2) + ); + + // We assume highway/cycleway of a cycle network to be safer (for bicycle network relations, their network is copied to way in postLoad) + // this uses a OR since you don't want to apply the safety multiplier more than once. + // Signed bicycle_roads and cyclestreets exist in traffic codes of some european countries. + // Tagging in OSM and on-the-ground use is varied, so just assume they are "somehow safer", too. + // In my test area ways often, but not always, have both tags. + // For simplicity these two concepts are handled together. + props.setMixinProperties( + new LogicalOrSpecifier( + "lcn=yes", + "rcn=yes", + "ncn=yes", + "bicycle_road=yes", + "cyclestreet=yes" + ), + ofBicycleSafety(0.7) + ); + + /* + * Automobile speeds in the United States: Based on my (mattwigway) personal experience, primarily in California + */ + props.setCarSpeed("highway=motorway", 29); // 29 m/s ~= 65 mph + props.setCarSpeed("highway=motorway_link", 15); // ~= 35 mph + props.setCarSpeed("highway=trunk", 24.6f); // ~= 55 mph + props.setCarSpeed("highway=trunk_link", 15); // ~= 35 mph + props.setCarSpeed("highway=primary", 20); // ~= 45 mph + props.setCarSpeed("highway=primary_link", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=secondary", 15); // ~= 35 mph + props.setCarSpeed("highway=secondary_link", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=tertiary", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=tertiary_link", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=living_street", 2.2f); // ~= 5 mph + + // generally, these will not allow cars at all, but the docs say + // "For roads used mainly/exclusively for pedestrians . . . which may allow access by + // motorised vehicles only for very limited periods of the day." + // http://wiki.openstreetmap.org/wiki/Key:highway + // This of course makes the street network time-dependent + props.setCarSpeed("highway=pedestrian", 2.2f); // ~= 5 mph + + props.setCarSpeed("highway=residential", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=unclassified", 11.2f); // ~= 25 mph + props.setCarSpeed("highway=service", 6.7f); // ~= 15 mph + props.setCarSpeed("highway=track", 4.5f); // ~= 10 mph + props.setCarSpeed("highway=road", 11.2f); // ~= 25 mph + + // default ~= 25 mph + props.defaultCarSpeed = 11.2f; + // 38 m/s ~= 85 mph ~= 137 kph + props.maxPossibleCarSpeed = 38f; + + /* special situations */ + + /* + * cycleway:left/right=lane/track/shared_lane permutations - no longer needed because left/right matching algorithm does this + */ + + /* cycleway:left=lane */ + /* cycleway:right=track */ + /* cycleway:left=track */ + /* cycleway:right=shared_lane */ + /* cycleway:left=shared_lane */ + /* cycleway:right=lane, cycleway:left=track */ + /* cycleway:right=lane, cycleway:left=shared_lane */ + /* cycleway:right=track, cycleway:left=lane */ + /* cycleway:right=track, cycleway:left=shared_lane */ + /* cycleway:right=shared_lane, cycleway:left=lane */ + /* cycleway:right=shared_lane, cycleway:left=track */ + + /* surface=* mixins */ + + /* + * The following tags have been removed from surface weights because they are no more of an impedence to bicycling than a paved surface + * surface=paving_stones surface=fine_gravel (sounds counter-intuitive but see the definition on the OSM Wiki) surface=tartan (this what + * running tracks are usually made of) + */ + + props.setMixinProperties("surface=unpaved", ofBicycleSafety(1.18)); + props.setMixinProperties("surface=compacted", ofBicycleSafety(1.18)); + props.setMixinProperties("surface=wood", ofBicycleSafety(1.18)); + + props.setMixinProperties("surface=cobblestone", ofBicycleSafety(1.3)); + props.setMixinProperties("surface=sett", ofBicycleSafety(1.3)); + props.setMixinProperties("surface=unhewn_cobblestone", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=grass_paver", ofBicycleSafety(1.3)); + props.setMixinProperties("surface=pebblestone", ofBicycleSafety(1.3)); + // Can be slick if wet, but otherwise not unfavorable to bikes + props.setMixinProperties("surface=metal", ofBicycleSafety(1.3)); + props.setMixinProperties("surface=ground", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=dirt", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=earth", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=grass", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=mud", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=woodchip", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=gravel", ofBicycleSafety(1.5)); + props.setMixinProperties("surface=artifical_turf", ofBicycleSafety(1.5)); + + /* sand is deadly for bikes */ + props.setMixinProperties("surface=sand", ofBicycleSafety(100)); + + /* Portland-local mixins */ + + props.setMixinProperties("foot=discouraged", ofWalkSafety(3)); + props.setMixinProperties("bicycle=discouraged", ofBicycleSafety(3)); + + props.setMixinProperties("foot=use_sidepath", ofWalkSafety(5)); + props.setMixinProperties("bicycle=use_sidepath", ofBicycleSafety(5)); + + populateNotesAndNames(props); + + // slope overrides + props.setSlopeOverride(new BestMatchSpecifier("bridge=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("embankment=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("cutting=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("tunnel=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("location=underground"), true); + props.setSlopeOverride(new BestMatchSpecifier("indoor=yes"), true); + } + + public void populateNotesAndNames(WayPropertySet props) { + /* and the notes */ + // TODO: The curly brackets in the string below mean that the CreativeNamer should substitute in OSM tag values. + // However they are not taken into account when passed to the translation function. + // props.createNotes("wheelchair:description=*", "{wheelchair:description}", StreetNotesService.WHEELCHAIR_MATCHER); + // TODO: The two entries below produce lots of spurious notes (because of OSM mapper comments) + // props.createNotes("note=*", "{note}", StreetNotesService.ALWAYS_MATCHER); + // props.createNotes("notes=*", "{notes}", StreetNotesService.ALWAYS_MATCHER); + props.createNotes( + "RLIS:bicycle=caution_area", + "note.caution", + StreetNotesService.BICYCLE_MATCHER + ); + props.createNotes( + "CCGIS:bicycle=caution_area", + "note.caution", + StreetNotesService.BICYCLE_MATCHER + ); + // TODO: Maybe we should apply the following notes only for car/bike + props.createNotes("surface=unpaved", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes( + "surface=compacted", + "note.unpaved_surface", + StreetNotesService.ALWAYS_MATCHER + ); + props.createNotes("surface=ground", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes("surface=dirt", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes("surface=earth", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes("surface=grass", "note.unpaved_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes("surface=mud", "note.muddy_surface", StreetNotesService.ALWAYS_MATCHER); + props.createNotes("toll=yes", "note.toll", StreetNotesService.DRIVING_MATCHER); + props.createNotes("toll:motorcar=yes", "note.toll", StreetNotesService.DRIVING_MATCHER); + + /* and some names */ + // Basics + props.createNames("highway=cycleway", "name.bike_path"); + props.createNames("cycleway=track", "name.bike_path"); + props.createNames("highway=pedestrian", "name.pedestrian_path"); + props.createNames("highway=pedestrian;area=yes", "name.pedestrian_area"); + props.createNames("highway=path", "name.path"); + props.createNames("highway=footway", "name.pedestrian_path"); + props.createNames("highway=bridleway", "name.bridleway"); + props.createNames("highway=footway;bicycle=no", "name.pedestrian_path"); + + // Platforms + props.createNames("otp:route_ref=*", "name.otp_route_ref"); + props.createNames("highway=platform;ref=*", "name.platform_ref"); + props.createNames("railway=platform;ref=*", "name.platform_ref"); + props.createNames("railway=platform;highway=footway;footway=sidewalk", "name.platform"); + props.createNames("railway=platform;highway=path;path=sidewalk", "name.platform"); + props.createNames("railway=platform;highway=pedestrian", "name.platform"); + props.createNames("railway=platform;highway=path", "name.platform"); + props.createNames("railway=platform;highway=footway", "name.platform"); + props.createNames("highway=platform", "name.platform"); + props.createNames("railway=platform", "name.platform"); + props.createNames("railway=platform;highway=footway;bicycle=no", "name.platform"); + + // Bridges/Tunnels + props.createNames("highway=pedestrian;bridge=*", "name.footbridge"); + props.createNames("highway=path;bridge=*", "name.footbridge"); + props.createNames("highway=footway;bridge=*", "name.footbridge"); + + props.createNames("highway=pedestrian;tunnel=*", "name.underpass"); + props.createNames("highway=path;tunnel=*", "name.underpass"); + props.createNames("highway=footway;tunnel=*", "name.underpass"); + + // Basic Mappings + props.createNames("highway=motorway", "name.road"); + props.createNames("highway=motorway_link", "name.ramp"); + props.createNames("highway=trunk", "name.road"); + props.createNames("highway=trunk_link", "name.ramp"); + + props.createNames("highway=primary", "name.road"); + props.createNames("highway=primary_link", "name.link"); + props.createNames("highway=secondary", "name.road"); + props.createNames("highway=secondary_link", "name.link"); + props.createNames("highway=tertiary", "name.road"); + props.createNames("highway=tertiary_link", "name.link"); + props.createNames("highway=unclassified", "name.road"); + props.createNames("highway=residential", "name.road"); + props.createNames("highway=living_street", "name.road"); + props.createNames("highway=road", "name.road"); + props.createNames("highway=service", "name.service_road"); + props.createNames("highway=service;service=alley", "name.alley"); + props.createNames("highway=service;service=parking_aisle", "name.parking_aisle"); + props.createNames("highway=byway", "name.byway"); + props.createNames("highway=track", "name.track"); + + props.createNames("highway=footway;footway=sidewalk", "name.sidewalk"); + props.createNames("highway=path;path=sidewalk", "name.sidewalk"); + + props.createNames("highway=steps", "name.steps"); + + props.createNames("amenity=bicycle_parking;name=*", "name.bicycle_parking_name"); + props.createNames("amenity=bicycle_parking", "name.bicycle_parking"); + + props.createNames("amenity=parking;name=*", "name.park_and_ride_name"); + props.createNames("amenity=parking", "name.park_and_ride_station"); + } + + public boolean doesTagValueDisallowThroughTraffic(String tagValue) { return ( "no".equals(tagValue) || "destination".equals(tagValue) || @@ -22,20 +731,20 @@ default boolean doesTagValueDisallowThroughTraffic(String tagValue) { ); } - default float getCarSpeedForWay(OsmWithTags way, boolean backward) { + public float getCarSpeedForWay(OsmWithTags way, boolean backward) { return way.getOsmProvider().getWayPropertySet().getCarSpeedForWay(way, backward); } - default Float getMaxUsedCarSpeed(WayPropertySet wayPropertySet) { + public Float getMaxUsedCarSpeed(WayPropertySet wayPropertySet) { return wayPropertySet.maxUsedCarSpeed; } - default boolean isGeneralNoThroughTraffic(OsmWithTags way) { + public boolean isGeneralNoThroughTraffic(OsmWithTags way) { String access = way.getTag("access"); return doesTagValueDisallowThroughTraffic(access); } - default boolean isVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String vehicle = way.getTag("vehicle"); if (vehicle != null) { return doesTagValueDisallowThroughTraffic(vehicle); @@ -47,7 +756,7 @@ default boolean isVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { /** * Returns true if through traffic for motor vehicles is not allowed. */ - default boolean isMotorVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isMotorVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String motorVehicle = way.getTag("motor_vehicle"); if (motorVehicle != null) { return doesTagValueDisallowThroughTraffic(motorVehicle); @@ -59,7 +768,7 @@ default boolean isMotorVehicleThroughTrafficExplicitlyDisallowed(OsmWithTags way /** * Returns true if through traffic for bicycle is not allowed. */ - default boolean isBicycleNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isBicycleThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String bicycle = way.getTag("bicycle"); if (bicycle != null) { return doesTagValueDisallowThroughTraffic(bicycle); @@ -71,7 +780,7 @@ default boolean isBicycleNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { /** * Returns true if through traffic for walk is not allowed. */ - default boolean isWalkNoThroughTrafficExplicitlyDisallowed(OsmWithTags way) { + public boolean isWalkThroughTrafficExplicitlyDisallowed(OsmWithTags way) { String foot = way.getTag("foot"); if (foot != null) { return doesTagValueDisallowThroughTraffic(foot); diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapperSource.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapperSource.java index b40c2e7f75a..98593ef70d0 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapperSource.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapperSource.java @@ -18,7 +18,7 @@ public enum OsmTagMapperSource { public OsmTagMapper getInstance() { return switch (this) { - case DEFAULT -> new DefaultMapper(); + case DEFAULT -> new OsmTagMapper(); case NORWAY -> new NorwayMapper(); case UK -> new UKMapper(); case FINLAND -> new FinlandMapper(); diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/PortlandMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/PortlandMapper.java index 98379852689..7da8f8ca886 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/PortlandMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/PortlandMapper.java @@ -9,7 +9,7 @@ import org.opentripplanner.osm.wayproperty.specifier.Condition.GreaterThan; import org.opentripplanner.osm.wayproperty.specifier.ExactMatchSpecifier; -class PortlandMapper implements OsmTagMapper { +class PortlandMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -57,7 +57,6 @@ public void populateProperties(WayPropertySet props) { // Max speed limit in Oregon is 70 mph ~= 113kmh ~= 31.3m/s props.maxPossibleCarSpeed = 31.4f; - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } } diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java index 08531ce051d..35a575b00c8 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java @@ -21,9 +21,9 @@ * * @author marcusyoung * @see OsmTagMapper - * @see DefaultMapper + * @see OsmTagMapper */ -class UKMapper implements OsmTagMapper { +class UKMapper extends OsmTagMapper { @Override public void populateProperties(WayPropertySet props) { @@ -79,7 +79,6 @@ public void populateProperties(WayPropertySet props) { props.setProperties("indoor=area", pedestrianWayProperties); props.setProperties("indoor=corridor", pedestrianWayProperties); - // Read the rest from the default set - new DefaultMapper().populateProperties(props); + super.populateProperties(props); } } diff --git a/application/src/main/java/org/opentripplanner/osm/wayproperty/specifier/BestMatchSpecifier.java b/application/src/main/java/org/opentripplanner/osm/wayproperty/specifier/BestMatchSpecifier.java index 94dbab99b26..7dacc61e6b8 100644 --- a/application/src/main/java/org/opentripplanner/osm/wayproperty/specifier/BestMatchSpecifier.java +++ b/application/src/main/java/org/opentripplanner/osm/wayproperty/specifier/BestMatchSpecifier.java @@ -26,6 +26,10 @@ public class BestMatchSpecifier implements OsmSpecifier { public static final int NO_MATCH_SCORE = 0; private final Condition[] conditions; + /** + * @deprecated Logic is fuzzy and unpredictable, use ExactMatchSpecifier instead + */ + @Deprecated public BestMatchSpecifier(String spec) { conditions = OsmSpecifier.parseConditions(spec, ";"); } diff --git a/application/src/main/java/org/opentripplanner/routing/alertpatch/AlertUrl.java b/application/src/main/java/org/opentripplanner/routing/alertpatch/AlertUrl.java index b13fad6e97b..fcce3720538 100644 --- a/application/src/main/java/org/opentripplanner/routing/alertpatch/AlertUrl.java +++ b/application/src/main/java/org/opentripplanner/routing/alertpatch/AlertUrl.java @@ -1,7 +1,10 @@ package org.opentripplanner.routing.alertpatch; -public class AlertUrl { +import java.util.Objects; +import javax.annotation.Nullable; - public String uri; - public String label; +public record AlertUrl(String uri, @Nullable String label) { + public AlertUrl { + Objects.requireNonNull(uri); + } } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index a81dd4e7083..0fef72f7b3e 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -1,11 +1,12 @@ package org.opentripplanner.routing.algorithm.mapping; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toOtpDomainCost; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toOtpDomainCost; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -38,8 +39,8 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.request.StreetSearchRequestMapper; +import org.opentripplanner.street.search.state.EdgeTraverser; import org.opentripplanner.street.search.state.State; -import org.opentripplanner.street.search.state.StateEditor; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.service.TransitService; @@ -360,24 +361,15 @@ private List mapNonTransitLeg( .build() ); } else { - StateEditor se = new StateEditor(edges.get(0).getFromVertex(), transferStreetRequest); - se.setTimeSeconds(createZonedDateTime(pathLeg.fromTime()).toEpochSecond()); - - State s = se.makeState(); - ArrayList transferStates = new ArrayList<>(); - transferStates.add(s); - for (Edge e : edges) { - var states = e.traverse(s); - if (State.isEmpty(states)) { - s = null; - } else { - transferStates.add(states[0]); - s = states[0]; - } - } - - State[] states = transferStates.toArray(new State[0]); - var graphPath = new GraphPath<>(states[states.length - 1]); + var legTransferSearchRequest = transferStreetRequest + .copyOf(createZonedDateTime(pathLeg.fromTime()).toInstant()) + .build(); + var initialStates = State.getInitialStates( + Set.of(edges.getFirst().getFromVertex()), + legTransferSearchRequest + ); + var state = EdgeTraverser.traverseEdges(initialStates, edges); + var graphPath = new GraphPath<>(state.get()); Itinerary subItinerary = graphPathToItineraryMapper.generateItinerary(graphPath); diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/DefaultAccessEgress.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/DefaultAccessEgress.java index 3f68d91321e..584e690d3ee 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/DefaultAccessEgress.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/DefaultAccessEgress.java @@ -3,7 +3,7 @@ import java.util.Objects; import org.opentripplanner.framework.model.TimeAndCost; import org.opentripplanner.raptor.api.model.RaptorConstants; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.street.search.state.State; /** diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 175c782ab22..32d54787e6f 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -4,14 +4,15 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.Set; import org.locationtech.jts.geom.Coordinate; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.preference.WalkPreferences; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.EdgeTraverser; -import org.opentripplanner.street.search.state.StateEditor; +import org.opentripplanner.street.search.state.State; import org.opentripplanner.utils.logging.Throttle; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; @@ -84,10 +85,8 @@ public Optional asRaptorTransfer(StreetSearchRequest request) { ); } - StateEditor se = new StateEditor(edges.get(0).getFromVertex(), request); - se.setTimeSeconds(0); - - var state = EdgeTraverser.traverseEdges(se.makeState(), edges); + var initialStates = State.getInitialStates(Set.of(edges.getFirst().getFromVertex()), request); + var state = EdgeTraverser.traverseEdges(initialStates, edges); return state.map(s -> new DefaultRaptorTransfer( diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java index 43faa3f0c40..1d4827a4ef6 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java @@ -3,6 +3,7 @@ import javax.annotation.Nullable; import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/IndexBasedFactorStrategy.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/IndexBasedFactorStrategy.java index 152f199248c..9fac19cd940 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/IndexBasedFactorStrategy.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/IndexBasedFactorStrategy.java @@ -1,6 +1,7 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; import java.util.Arrays; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; /** * This class keep a facto for each index and the minimum factor for fast retrieval during Raptor diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunction.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunction.java index b636b26bebe..f231acf9270 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunction.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunction.java @@ -2,6 +2,7 @@ import java.util.Objects; import org.opentripplanner.framework.model.Cost; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.routing.api.request.framework.LinearFunctionSerialization; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/SingleValueFactorStrategy.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/SingleValueFactorStrategy.java index 59cf04eb2db..3e5b868afdb 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/SingleValueFactorStrategy.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/SingleValueFactorStrategy.java @@ -1,5 +1,7 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; + /** * This {@link FactorStrategy} keep a single value and use it every time the factor is needed. The * {@link #minFactor()} return the same value. diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java index d68d2230a5c..693cac45031 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java @@ -1,6 +1,7 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; import org.opentripplanner.routing.api.request.preference.AccessibilityPreferences; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 1074724a90c..c5a75b02bbc 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -12,9 +12,11 @@ import org.opentripplanner.raptor.api.model.GeneralizedCostRelaxFunction; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.model.RelaxFunction; import org.opentripplanner.raptor.api.request.DebugRequestBuilder; +import org.opentripplanner.raptor.api.request.MultiCriteriaRequest; import org.opentripplanner.raptor.api.request.Optimization; import org.opentripplanner.raptor.api.request.PassThroughPoint; import org.opentripplanner.raptor.api.request.RaptorRequest; @@ -22,10 +24,10 @@ import org.opentripplanner.raptor.api.request.RaptorViaLocation; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.DebugEventType; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; +import org.opentripplanner.routing.api.request.preference.TransitPreferences; import org.opentripplanner.routing.api.request.via.ViaLocation; import org.opentripplanner.transit.model.network.grouppriority.DefaultTransitGroupPriorityCalculator; @@ -128,15 +130,20 @@ private RaptorRequest doMap() { var pt = preferences.transit(); var r = pt.raptor(); - // Note! If a pass-through-point exists, then the transit-group-priority feature is disabled - - // TODO - We need to handle via locations that are not pass-through-points here if (hasPassThroughOnly()) { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); + } else if (hasViaLocationsOnly()) { + builder.searchParams().addViaLocations(mapViaLocations()); + // relax transit group priority can be used with via-visit-stop, but not with pass-through + if (pt.isRelaxTransitGroupPrioritySet()) { + mapRelaxTransitGroupPriority(mcBuilder, pt); + } + } else if (pt.isRelaxTransitGroupPrioritySet()) { + mapRelaxTransitGroupPriority(mcBuilder, pt); + } else { + // The deprecated relaxGeneralizedCostAtDestination is only enabled, if there is no + // via location and the relaxTransitGroupPriority is not used (Normal). r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); - } else if (!pt.relaxTransitGroupPriority().isNormal()) { - mcBuilder.withTransitPriorityCalculator(new DefaultTransitGroupPriorityCalculator()); - mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); } }); @@ -160,10 +167,6 @@ private RaptorRequest doMap() { .addAccessPaths(accessPaths) .addEgressPaths(egressPaths); - if (hasViaLocationsOnly()) { - builder.searchParams().addViaLocations(mapViaLocations()); - } - var raptorDebugging = request.journey().transit().raptorDebugging(); if (raptorDebugging.isEnabled()) { @@ -199,11 +202,17 @@ private RaptorRequest doMap() { } private boolean hasPassThroughOnly() { - return request.getViaLocations().stream().allMatch(ViaLocation::isPassThroughLocation); + return ( + request.isViaSearch() && + request.getViaLocations().stream().allMatch(ViaLocation::isPassThroughLocation) + ); } private boolean hasViaLocationsOnly() { - return request.getViaLocations().stream().noneMatch(ViaLocation::isPassThroughLocation); + return ( + request.isViaSearch() && + request.getViaLocations().stream().noneMatch(ViaLocation::isPassThroughLocation) + ); } private boolean hasViaLocationsAndPassThroughLocations() { @@ -265,6 +274,14 @@ private int relativeTime(Instant time) { return (int) (time.getEpochSecond() - transitSearchTimeZeroEpocSecond); } + private static void mapRelaxTransitGroupPriority( + MultiCriteriaRequest.Builder mcBuilder, + TransitPreferences pt + ) { + mcBuilder.withTransitPriorityCalculator(new DefaultTransitGroupPriorityCalculator()); + mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); + } + private static void addLogListenerForEachEventTypeRequested( DebugRequestBuilder target, DebugEventType type, diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java index b0d2b009a0f..a056efe87df 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java @@ -13,13 +13,13 @@ import java.util.Set; import javax.annotation.Nullable; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitTuningParameters; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripPatternForDate; import org.opentripplanner.routing.algorithm.raptoradapter.transit.constrainedtransfer.ConstrainedTransfersForPatterns; import org.opentripplanner.routing.algorithm.raptoradapter.transit.constrainedtransfer.TransferIndexGenerator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.RaptorRequestTransferCache; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopTransferPriority; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TransferWaitTimeCostCalculator.java b/application/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TransferWaitTimeCostCalculator.java index a5336999b09..ee33f1d86c3 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TransferWaitTimeCostCalculator.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TransferWaitTimeCostCalculator.java @@ -1,6 +1,6 @@ package org.opentripplanner.routing.algorithm.transferoptimization.model; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; /** * This calculator uses the {@code minSafeTransferTime}(t0) and an inverse log function to calculate diff --git a/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java b/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java index 14dc7ade982..51af4a27f79 100644 --- a/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java +++ b/application/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java @@ -45,18 +45,10 @@ public static List getAlternativeLegs( Leg leg, Integer numberLegs, TransitService transitService, - boolean searchBackward, + NavigationDirection direction, AlternativeLegsFilter filter ) { - return getAlternativeLegs( - leg, - numberLegs, - transitService, - searchBackward, - filter, - false, - false - ); + return getAlternativeLegs(leg, numberLegs, transitService, direction, filter, false, false); } /** @@ -66,9 +58,8 @@ public static List getAlternativeLegs( * @param numberLegs The number of alternative legs requested. If fewer legs are found, * only the found legs are returned. * @param transitService The transit service used for the search - * @param includeDepartBefore Boolean indicating whether the alternative legs should depart - * earlier or later than the original leg True if earlier, false if - * later. + * @param direction Indicating whether the alternative legs should depart before or + * after than the original. * @param filter AlternativeLegsFilter indicating which properties of the original * leg should not change in the alternative legs * @param exactOriginStop Boolean indicating whether the exact departure stop of the original @@ -82,7 +73,7 @@ public static List getAlternativeLegs( Leg leg, Integer numberLegs, TransitService transitService, - boolean includeDepartBefore, + NavigationDirection direction, AlternativeLegsFilter filter, boolean exactOriginStop, boolean exactDestinationStop @@ -105,7 +96,7 @@ public static List getAlternativeLegs( ScheduledTransitLeg::getStartTime ); - if (includeDepartBefore) { + if (direction == NavigationDirection.PREVIOUS) { legComparator = legComparator.reversed(); } @@ -119,13 +110,7 @@ public static List getAlternativeLegs( .distinct() .flatMap(tripPattern -> withBoardingAlightingPositions(origins, destinations, tripPattern)) .flatMap(t -> - generateLegs( - transitService, - t, - leg.getStartTime(), - leg.getServiceDate(), - includeDepartBefore - ) + generateLegs(transitService, t, leg.getStartTime(), leg.getServiceDate(), direction) ) .filter(Predicate.not(leg::isPartiallySameTransitLeg)) .sorted(legComparator) @@ -142,7 +127,7 @@ private static Stream generateLegs( TripPatternBetweenStops tripPatternBetweenStops, ZonedDateTime departureTime, LocalDate originalDate, - boolean includeDepartBefore + NavigationDirection direction ) { TripPattern pattern = tripPatternBetweenStops.tripPattern; int boardingPosition = tripPatternBetweenStops.positions.boardingPosition; @@ -155,7 +140,7 @@ private static Stream generateLegs( tts.getServiceDayMidnight() + tts.getRealtimeDeparture() ); - if (includeDepartBefore) { + if (direction == NavigationDirection.PREVIOUS) { comparator = comparator.reversed(); } @@ -185,7 +170,7 @@ private static Stream generateLegs( continue; } - boolean departureTimeInRange = includeDepartBefore + boolean departureTimeInRange = direction == NavigationDirection.PREVIOUS ? tripTimes.getDepartureTime(boardingPosition) <= secondsSinceMidnight : tripTimes.getDepartureTime(boardingPosition) >= secondsSinceMidnight; diff --git a/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java b/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java new file mode 100644 index 00000000000..8254e3e28b7 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/routing/alternativelegs/NavigationDirection.java @@ -0,0 +1,16 @@ +package org.opentripplanner.routing.alternativelegs; + +/** + * This enum describes how the user navigates on a list of items. + */ +public enum NavigationDirection { + /** + * Get the next set of items. + */ + NEXT, + + /** + * Get the previous set of items. + */ + PREVIOUS, +} diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 8e649b3be49..4e36b1b58db 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -292,8 +292,9 @@ public List getViaLocations() { return via; } - public void setViaLocations(final List via) { + public RouteRequest setViaLocations(final List via) { this.via = via; + return this; } /** diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java index f15b47216e5..a237dd53527 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java @@ -137,6 +137,10 @@ public CostLinearFunction relaxTransitGroupPriority() { return relaxTransitGroupPriority; } + public boolean isRelaxTransitGroupPrioritySet() { + return !relaxTransitGroupPriority.isNormal(); + } + /** * When true, real-time updates are ignored during this search. */ diff --git a/application/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java b/application/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java index 93fe12a1c7c..fa65dd86987 100644 --- a/application/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java +++ b/application/src/main/java/org/opentripplanner/service/vehiclerental/VehicleRentalService.java @@ -38,4 +38,14 @@ List getVehicleRentalStationForEnvelope( double maxLon, double maxLat ); + + /** + * Gets all vehicle rental places inside an envelope. + */ + List getVehicleRentalPlacesForEnvelope( + double minLon, + double minLat, + double maxLon, + double maxLat + ); } diff --git a/application/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java b/application/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java index 996f4cbcc9d..1f66d654cdb 100644 --- a/application/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java +++ b/application/src/main/java/org/opentripplanner/service/vehiclerental/internal/DefaultVehicleRentalService.java @@ -119,4 +119,23 @@ private Stream getVehicleRentalStationsAsStream() { .filter(VehicleRentalStation.class::isInstance) .map(VehicleRentalStation.class::cast); } + + @Override + public List getVehicleRentalPlacesForEnvelope( + double minLon, + double minLat, + double maxLon, + double maxLat + ) { + Envelope envelope = new Envelope( + new Coordinate(minLon, minLat), + new Coordinate(maxLon, maxLat) + ); + + Stream vehicleRentalPlaceStream = getVehicleRentalPlaces() + .stream() + .filter(vr -> envelope.contains(new Coordinate(vr.getLongitude(), vr.getLatitude()))); + + return vehicleRentalPlaceStream.toList(); + } } diff --git a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java index a8096d806ba..a10f8f8ef7b 100644 --- a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -52,7 +52,7 @@ public static void main(String[] args) { try { Thread.currentThread().setName("main"); CommandLineParameters params = parseAndValidateCmdLine(args); - OtpStartupInfo.logInfo(); + OtpStartupInfo.logInfo(params.logTaskInfo()); startOTPServer(params); } catch (OtpAppException ae) { LOG.error(ae.getMessage(), ae); diff --git a/application/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java b/application/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java index c601846d889..ddcc2c60212 100644 --- a/application/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java +++ b/application/src/main/java/org/opentripplanner/standalone/OtpStartupInfo.java @@ -34,13 +34,18 @@ private static String info() { ); } - public static void logInfo() { + public static void logInfo(String cliTaskInfo) { // This is good when aggregating logs across multiple load balanced instances of OTP // Hint: a regexp filter like "^OTP (START|SHUTTING)" will list nodes going up/down - LOG.info("OTP STARTING UP ({}) using Java {}", projectInfo().getVersionString(), javaVersion()); + LOG.info( + "OTP STARTING UP - {} - {} - Java {}", + cliTaskInfo, + projectInfo().getVersionString(), + javaVersion() + ); ApplicationShutdownSupport.addShutdownHook( "server-shutdown-info", - () -> LOG.info("OTP SHUTTING DOWN ({})", projectInfo().getVersionString()) + () -> LOG.info("OTP SHUTTING DOWN - {} - {}", cliTaskInfo, projectInfo().getVersionString()) ); LOG.info(NEW_LINE + "{}", info()); } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java b/application/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java index 3513ec252ea..2324ce325d9 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java @@ -201,6 +201,21 @@ public boolean doServe() { return load || (serve && doBuildTransit()); } + public String logTaskInfo() { + var mainCommands = new ArrayList(); + if (doBuildStreet() & doBuildTransit()) { + mainCommands.add("Build Street & Transit Graph"); + } else if (doBuildStreet()) { + mainCommands.add("Build Street Graph"); + } else if (doBuildTransit()) { + mainCommands.add("Build Transit Graph"); + } + if (doServe()) { + mainCommands.add("Run Server"); + } + return String.join(", ", mainCommands); + } + /** * @param port a port that we plan to bind to * @throws ParameterException if that port is not available diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETGooglePubsubUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETGooglePubsubUpdaterConfig.java index 65fb7bb2c11..3396e56f6e3 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETGooglePubsubUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETGooglePubsubUpdaterConfig.java @@ -1,6 +1,7 @@ package org.opentripplanner.standalone.config.routerconfig.updaters; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_1; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import static org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters.INITIAL_GET_DATA_TIMEOUT; import static org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters.RECONNECT_PERIOD; @@ -79,6 +80,11 @@ If this parameter is set, the updater will be marked as initialized (primed) onl .of("fuzzyTripMatching") .since(V2_1) .summary("If the trips should be matched fuzzily.") + .asBoolean(false), + c + .of("producerMetrics") + .since(V2_7) + .summary("If failure, success, and warning metrics should be collected per producer.") .asBoolean(false) ); } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETUpdaterConfig.java index 4402ba83b7e..1b0eb2e4420 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETUpdaterConfig.java @@ -2,6 +2,7 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -43,7 +44,12 @@ public static SiriETUpdaterParameters create(String configRef, NodeAdapter c) { .since(V2_0) .summary("If the fuzzy trip matcher should be used to match trips.") .asBoolean(false), - HttpHeadersConfig.headers(c, V2_3) + HttpHeadersConfig.headers(c, V2_3), + c + .of("producerMetrics") + .since(V2_7) + .summary("If failure, success, and warning metrics should be collected per producer.") + .asBoolean(false) ); } } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/sources/VehicleRentalSourceFactory.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/sources/VehicleRentalSourceFactory.java index aa548977c7e..e58b5e96ae8 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/sources/VehicleRentalSourceFactory.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/sources/VehicleRentalSourceFactory.java @@ -1,16 +1,20 @@ package org.opentripplanner.standalone.config.routerconfig.updaters.sources; +import static org.opentripplanner.standalone.config.framework.json.EnumMapper.docEnumValueList; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V1_5; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_1; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; +import java.util.Set; import org.opentripplanner.ext.smoovebikerental.SmooveBikeRentalDataSourceParameters; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.routerconfig.updaters.HttpHeadersConfig; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.updater.vehicle_rental.datasources.params.VehicleRentalDataSourceParameters; /** @@ -43,13 +47,15 @@ public VehicleRentalDataSourceParameters create() { headers(), network(), geofencingZones(), - overloadingAllowed() + overloadingAllowed(), + rentalPickupTypes() ); case SMOOVE -> new SmooveBikeRentalDataSourceParameters( url(), network(), overloadingAllowed(), - headers() + headers(), + rentalPickupTypes() ); }; } @@ -121,4 +127,13 @@ private boolean geofencingZones() { ) .asBoolean(false); } + + private Set rentalPickupTypes() { + return c + .of("rentalPickupTypes") + .since(V2_7) + .summary(RentalPickupType.STATION.typeDescription()) + .description(docEnumValueList(RentalPickupType.values())) + .asEnumSet(RentalPickupType.class, RentalPickupType.ALL); + } } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index b606bcd9962..ee24e87f07c 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -2,13 +2,16 @@ import java.io.IOException; import java.io.ObjectOutputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; import org.opentripplanner.framework.geometry.CompactLineStringUtils; @@ -976,6 +979,47 @@ static int defaultMillimeterLength(LineString geometry) { return (int) (SphericalDistanceLibrary.length(geometry) * 1000); } + /** + * Helper method for {@link #splitStatesAfterHavingExitedNoDropOffZoneWhenReverseSearching}. + * Create a single new state, exiting a no-drop-off zone, in reverse, and continuing + * on a rental vehicle in the known network, or an unknown network if network is null, + * unless the known network is not accepted by the provided {@link RoutingPreferences}. + * @param s0 The parent state (i.e. the following state, as we are in reverse) + * @param network Network id, or null if unknown + * @param preferences Active {@link RoutingPreferences} + * @return Newly generated {@link State}, or null if the state would have been forbidden. + */ + private State createStateAfterHavingExitedNoDropOffZoneWhenReverseSearching( + State s0, + String network, + RoutingPreferences preferences + ) { + var edit = doTraverse(s0, TraverseMode.WALK, false); + if (edit != null) { + edit.dropFloatingVehicle(s0.vehicleRentalFormFactor(), network, s0.getRequest().arriveBy()); + if (network != null) { + edit.resetStartedInNoDropOffZone(); + } + State state = edit.makeState(); + if (state != null && network != null) { + var rentalPreferences = preferences.rental(state.currentMode()); + var allowedNetworks = rentalPreferences.allowedNetworks(); + var bannedNetworks = rentalPreferences.bannedNetworks(); + if (allowedNetworks.isEmpty()) { + if (bannedNetworks.contains(network)) { + return null; + } + } else { + if (!allowedNetworks.contains(network)) { + return null; + } + } + } + return state; + } + return null; + } + /** * A very special case: an arriveBy rental search has started in a no-drop-off zone * we don't know yet which rental network we will end up using. @@ -987,25 +1031,38 @@ static int defaultMillimeterLength(LineString geometry) { * zone applies to where we pick up a vehicle with a specific network. */ private State[] splitStatesAfterHavingExitedNoDropOffZoneWhenReverseSearching(State s0) { - var networks = Stream.concat( - // null is a special rental network that speculatively assumes that you can take any vehicle - // you have to check in the rental edge if this has search has been started in a no-drop off zone - Stream.of((String) null), - tov.rentalRestrictions().noDropOffNetworks().stream() - ); + var preferences = s0.getRequest().preferences(); + var states = new ArrayList(); + + // Also include a state which continues walking, because the vehicle rental states are + // speculation. It is possible that the rental states don't end up at the target at all + // due to mode limitations or not finding a place to pick up the rental vehicle, or that + // the rental possibility is simply more expensive than walking. + StateEditor walking = doTraverse(s0, TraverseMode.WALK, false); + if (walking != null) { + states.add(walking.makeState()); + } - var states = networks.map(network -> { - var edit = doTraverse(s0, TraverseMode.WALK, false); - if (edit != null) { - edit.dropFloatingVehicle(s0.vehicleRentalFormFactor(), network, s0.getRequest().arriveBy()); - if (network != null) { - edit.resetStartedInNoDropOffZone(); - } - return edit.makeState(); + boolean hasNetworkStates = false; + for (var network : tov.rentalRestrictions().noDropOffNetworks()) { + var state = createStateAfterHavingExitedNoDropOffZoneWhenReverseSearching( + s0, + network, + preferences + ); + if (state != null) { + states.add(state); + hasNetworkStates = true; } - return null; - }); - return State.ofStream(states); + } + if (hasNetworkStates) { + // null is a special rental network that speculatively assumes that you can take any vehicle + // you have to check in the rental edge if this has search has been started in a no-drop off zone + states.add( + createStateAfterHavingExitedNoDropOffZoneWhenReverseSearching(s0, null, preferences) + ); + } + return states.toArray(State[]::new); } /** diff --git a/application/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java b/application/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java index c93ea598256..df8933cd22d 100644 --- a/application/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java +++ b/application/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java @@ -124,6 +124,10 @@ public DataOverlayContext dataOverlayContext() { return dataOverlayContext; } + public StreetSearchRequestBuilder copyOf(Instant time) { + return copyOf(this).withStartTime(time); + } + public StreetSearchRequestBuilder copyOfReversed(Instant time) { return copyOf(this).withStartTime(time).withArriveBy(!arriveBy); } diff --git a/application/src/main/java/org/opentripplanner/street/search/state/EdgeTraverser.java b/application/src/main/java/org/opentripplanner/street/search/state/EdgeTraverser.java index 8755f014e14..502d014e358 100644 --- a/application/src/main/java/org/opentripplanner/street/search/state/EdgeTraverser.java +++ b/application/src/main/java/org/opentripplanner/street/search/state/EdgeTraverser.java @@ -2,7 +2,10 @@ import java.util.Collection; import java.util.Optional; +import org.opentripplanner.astar.model.ShortestPathTree; import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.street.search.strategy.DominanceFunctions; /** * This is a very reduced version of the A* algorithm: from an initial state a number of edges are @@ -14,24 +17,49 @@ */ public class EdgeTraverser { - public static Optional traverseEdges(final State s, final Collection edges) { - var state = s; + public static Optional traverseEdges( + final Collection initialStates, + final Collection edges + ) { + return traverseEdges(initialStates.toArray(new State[0]), edges); + } + + public static Optional traverseEdges( + final State[] initialStates, + final Collection edges + ) { + if (edges.isEmpty()) { + return Optional.of(initialStates[0]); + } + + // The shortest path tree is used to prune dominated parallel states. For example, + // CAR_PICKUP can return both a CAR/WALK state after each traversal of which only + // the optimal states need to be continued. + var dominanceFunction = new DominanceFunctions.MinimumWeight(); + var spt = new ShortestPathTree<>(dominanceFunction); + for (State initialState : initialStates) { + spt.add(initialState); + } + + Vertex lastVertex = null; + var isArriveBy = initialStates[0].getRequest().arriveBy(); for (Edge e : edges) { - var afterTraversal = e.traverse(state); - if (afterTraversal.length > 1) { - throw new IllegalStateException( - "Expected only a single state returned from edge %s but received %s".formatted( - e, - afterTraversal.length - ) - ); - } - if (State.isEmpty(afterTraversal)) { + var vertex = isArriveBy ? e.getToVertex() : e.getFromVertex(); + var fromStates = spt.getStates(vertex); + if (fromStates == null || fromStates.isEmpty()) { return Optional.empty(); - } else { - state = afterTraversal[0]; } + + for (State fromState : fromStates) { + var newToStates = e.traverse(fromState); + for (State newToState : newToStates) { + spt.add(newToState); + } + } + + lastVertex = isArriveBy ? e.getFromVertex() : e.getToVertex(); } - return Optional.ofNullable(state); + + return Optional.ofNullable(lastVertex).map(spt::getState); } } diff --git a/application/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java b/application/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java index be12be83548..0b269220ae1 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java +++ b/application/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java @@ -78,7 +78,7 @@ public static boolean isValidString(String value) throws IllegalArgumentExceptio /** * Concatenate feedId and id into a string. */ - public static String concatenateId(String feedId, String id) { + private static String concatenateId(String feedId, String id) { return feedId + ID_SEPARATOR + id; } diff --git a/application/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java b/application/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java index ec2451ea66d..66f695521c0 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java @@ -1,7 +1,5 @@ package org.opentripplanner.transit.model.network; -import static java.util.Objects.requireNonNullElseGet; - import java.util.ArrayList; import java.util.List; import java.util.function.UnaryOperator; @@ -219,15 +217,12 @@ private List generateHopGeometriesFromOriginalTripPattern() { // being replaced having a different number of stops. In that case the geometry will be // preserved up until the first mismatching stop, and a straight line will be used for // all segments after that. - int sizeOfShortestPattern = Math.min( - stopPattern.getSize(), - originalTripPattern.numberOfStops() - ); - List hopGeometries = new ArrayList<>(); - for (int i = 0; i < sizeOfShortestPattern - 1; i++) { - LineString hopGeometry = originalTripPattern.getHopGeometry(i); + for (int i = 0; i < stopPattern.getSize() - 1; i++) { + LineString hopGeometry = i < originalTripPattern.numberOfStops() - 1 + ? originalTripPattern.getHopGeometry(i) + : null; if (hopGeometry != null && stopPattern.sameStops(originalTripPattern.getStopPattern(), i)) { // Copy hop geometry from previous pattern @@ -236,15 +231,8 @@ private List generateHopGeometriesFromOriginalTripPattern() { hopGeometry != null && stopPattern.sameStations(originalTripPattern.getStopPattern(), i) ) { // Use old geometry but patch first and last point with new stops - var newStart = new Coordinate( - stopPattern.getStop(i).getCoordinate().longitude(), - stopPattern.getStop(i).getCoordinate().latitude() - ); - - var newEnd = new Coordinate( - stopPattern.getStop(i + 1).getCoordinate().longitude(), - stopPattern.getStop(i + 1).getCoordinate().latitude() - ); + var newStart = stopPattern.getStop(i).getCoordinate().asJtsCoordinate(); + var newEnd = stopPattern.getStop(i + 1).getCoordinate().asJtsCoordinate(); Coordinate[] coordinates = originalTripPattern.getHopGeometry(i).getCoordinates().clone(); coordinates[0].setCoordinate(newStart); diff --git a/application/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java b/application/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java index 0ded9d64dab..8dd581b5699 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java +++ b/application/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java @@ -29,7 +29,7 @@ public final class RegularStop private final ZoneId timeZone; - private final TransitMode gtfsVehicleType; + private final TransitMode vehicleType; private final SubMode netexVehicleSubmode; @@ -43,7 +43,7 @@ public final class RegularStop this.platformCode = builder.platformCode(); this.url = builder.url(); this.timeZone = builder.timeZone(); - this.gtfsVehicleType = builder.vehicleType(); + this.vehicleType = builder.vehicleType(); this.netexVehicleSubmode = SubMode.getOrBuildAndCacheForever(builder.netexVehicleSubmode()); this.boardingAreas = setOfNullSafe(builder.boardingAreas()); this.fareZones = setOfNullSafe(builder.fareZones()); @@ -102,8 +102,16 @@ public ZoneId getTimeZone() { */ @Override @Nullable - public TransitMode getGtfsVehicleType() { - return gtfsVehicleType; + public TransitMode getVehicleType() { + return vehicleType; + } + + /** + * Return {@code true} if the vehicle type is set in the import to be RAIL. Note! This does + * not check patterns visiting the stop. + */ + public boolean isRailStop() { + return vehicleType == TransitMode.RAIL; } public SubMode getNetexVehicleSubmode() { @@ -148,7 +156,7 @@ public boolean sameAs(RegularStop other) { Objects.equals(platformCode, other.platformCode) && Objects.equals(url, other.url) && Objects.equals(timeZone, other.timeZone) && - Objects.equals(gtfsVehicleType, other.gtfsVehicleType) && + Objects.equals(vehicleType, other.vehicleType) && Objects.equals(netexVehicleSubmode, other.netexVehicleSubmode) && Objects.equals(boardingAreas, other.boardingAreas) && Objects.equals(fareZones, other.fareZones) diff --git a/application/src/main/java/org/opentripplanner/transit/model/site/RegularStopBuilder.java b/application/src/main/java/org/opentripplanner/transit/model/site/RegularStopBuilder.java index 3b37a709078..5e6cd01618b 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/site/RegularStopBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/model/site/RegularStopBuilder.java @@ -26,7 +26,7 @@ public final class RegularStopBuilder private ZoneId timeZone; - private TransitMode gtfsVehicleType; + private TransitMode vehicleType; private String netexVehicleSubmode; @@ -45,7 +45,7 @@ public final class RegularStopBuilder this.platformCode = original.getPlatformCode(); this.url = original.getUrl(); this.timeZone = original.getTimeZone(); - this.gtfsVehicleType = original.getGtfsVehicleType(); + this.vehicleType = original.getVehicleType(); this.netexVehicleSubmode = original.getNetexVehicleSubmode().name(); } @@ -68,11 +68,11 @@ public RegularStopBuilder withUrl(I18NString url) { } public TransitMode vehicleType() { - return gtfsVehicleType; + return vehicleType; } public RegularStopBuilder withVehicleType(TransitMode vehicleType) { - this.gtfsVehicleType = vehicleType; + this.vehicleType = vehicleType; return this; } diff --git a/application/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java b/application/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java index ecd0cf440b6..c4e704b954d 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java +++ b/application/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java @@ -62,7 +62,7 @@ default String getPlatformCode() { } @Nullable - default TransitMode getGtfsVehicleType() { + default TransitMode getVehicleType() { return null; } 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 5f7cfa95524..f8d1e437a34 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -599,6 +599,18 @@ public List getTripOnServiceDates(TripOnServiceDateRequest re return getAllTripOnServiceDates().stream().filter(matcher::match).toList(); } + @Override + public boolean containsTrip(FeedScopedId id) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + Trip trip = currentSnapshot.getRealTimeAddedTrip(id); + if (trip != null) { + return true; + } + } + return this.timetableRepositoryIndex.containsTrip(id); + } + /** * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix * this when doing the issue #3030. @@ -731,8 +743,8 @@ public Map getServiceCodesRunningForDate() { * For each pattern visiting this {@link StopLocation} return its {@link TransitMode} */ private Stream getPatternModesOfStop(StopLocation stop) { - if (stop.getGtfsVehicleType() != null) { - return Stream.of(stop.getGtfsVehicleType()); + if (stop.getVehicleType() != null) { + return Stream.of(stop.getVehicleType()); } else { return getPatternsForStop(stop).stream().map(TripPattern::getMode); } 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 a57299d96ab..b81dd7d4f14 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -166,10 +166,9 @@ public TimetableRepository() { */ public void index() { if (index == null) { - LOG.info("Index transit model..."); - // the transit model indexing updates the site repository index (flex stops added to the stop index) + LOG.info("Index timetable repository..."); this.index = new TimetableRepositoryIndex(this); - LOG.info("Index transit model complete."); + LOG.info("Index timetable repository complete."); } } diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepositoryIndex.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepositoryIndex.java index dc618b0943f..ca52b710957 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepositoryIndex.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepositoryIndex.java @@ -59,7 +59,7 @@ class TimetableRepositoryIndex { private FlexIndex flexIndex = null; TimetableRepositoryIndex(TimetableRepository timetableRepository) { - LOG.info("Transit model index init..."); + LOG.info("Timetable repository index init..."); for (Agency agency : timetableRepository.getAgencies()) { this.agencyForId.put(agency.getId(), agency); @@ -113,7 +113,7 @@ class TimetableRepositoryIndex { } } - LOG.info("Transit Model index init complete."); + LOG.info("Timetable repository index init complete."); } Agency getAgencyForId(FeedScopedId id) { @@ -161,6 +161,16 @@ Trip getTripForId(FeedScopedId tripId) { return tripForId.get(tripId); } + /** + * Checks if the specified trip is contained within the index. + * + * @param tripId the {@link FeedScopedId} of the trip to check + * @return true if the trip exists in the index map, false otherwise + */ + boolean containsTrip(FeedScopedId tripId) { + return tripForId.containsKey(tripId); + } + TripOnServiceDate getTripOnServiceDateForTripAndDay(TripIdAndServiceDate tripIdAndServiceDate) { return tripOnServiceDateForTripAndDay.get(tripIdAndServiceDate); } 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 7be2a29065a..b77eb77d892 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -287,7 +287,7 @@ List stopTimesForPatternAtStop( /** * For a {@link StopLocationsGroup} get all child stops and get their modes. *

- * The mode is either taken from {@link StopLocation#getGtfsVehicleType()} (if non-null) + * The mode is either taken from {@link StopLocation#getVehicleType()} (if non-null) * or from the list of patterns that use the stop location. *

* The returning stream is ordered by the number of occurrences of the mode in the child stops. @@ -297,10 +297,10 @@ List stopTimesForPatternAtStop( /** * For a {@link StopLocation} return its modes. *

- * The mode is either taken from {@link StopLocation#getGtfsVehicleType()} (if non-null) + * The mode is either taken from {@link StopLocation#getVehicleType()} (if non-null) * or from the list of patterns that use the stop location. *

- * If {@link StopLocation#getGtfsVehicleType()} is null the returning stream is ordered by the number + * If {@link StopLocation#getVehicleType()} is null the returning stream is ordered by the number * of occurrences of the mode in the stop. *

* So, if more patterns of mode BUS than RAIL visit the stop, the result will be [BUS,RAIL]. @@ -320,4 +320,12 @@ List stopTimesForPatternAtStop( * @return - A list of TripOnServiceDates */ List getTripOnServiceDates(TripOnServiceDateRequest request); + + /** + * Checks if a trip with the given ID exists in the model. + * + * @param id the {@link FeedScopedId} of the trip to check + * @return true if the trip exists, false otherwise + */ + boolean containsTrip(FeedScopedId id); } diff --git a/application/src/main/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcher.java b/application/src/main/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcher.java index 96e2d25bbd8..05d0f814263 100644 --- a/application/src/main/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcher.java +++ b/application/src/main/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcher.java @@ -35,7 +35,9 @@ public GtfsRealtimeFuzzyTripMatcher(TransitService transitService) { } public TripDescriptor match(String feedId, TripDescriptor trip) { - if (trip.hasTripId()) { + if ( + trip.hasTripId() && transitService.containsTrip(new FeedScopedId(feedId, trip.getTripId())) + ) { // trip_id already exists return trip; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/AddedTripBuilder.java b/application/src/main/java/org/opentripplanner/updater/siri/AddedTripBuilder.java index a1ca89f5c83..aff1659653b 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/AddedTripBuilder.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/AddedTripBuilder.java @@ -56,6 +56,7 @@ class AddedTripBuilder { private final Function getTripPatternId; private final FeedScopedId tripId; private final Operator operator; + private final String dataSource; private final String lineRef; private final Route replacedRoute; private final LocalDate serviceDate; @@ -81,11 +82,14 @@ class AddedTripBuilder { Objects.requireNonNull(newServiceJourneyRef, "EstimatedVehicleJourneyCode is required"); tripId = entityResolver.resolveId(newServiceJourneyRef); - //OperatorRef of added trip + // OperatorRef of added trip Objects.requireNonNull(estimatedVehicleJourney.getOperatorRef(), "OperatorRef is required"); String operatorRef = estimatedVehicleJourney.getOperatorRef().getValue(); operator = entityResolver.resolveOperator(operatorRef); + // DataSource of added trip + dataSource = estimatedVehicleJourney.getDataSource(); + // LineRef of added trip Objects.requireNonNull(estimatedVehicleJourney.getLineRef(), "LineRef is required"); lineRef = estimatedVehicleJourney.getLineRef().getValue(); @@ -135,7 +139,8 @@ class AddedTripBuilder { boolean cancellation, String shortName, String headsign, - List replacedTrips + List replacedTrips, + String dataSource ) { this.transitService = transitService; this.entityResolver = entityResolver; @@ -155,20 +160,21 @@ class AddedTripBuilder { this.shortName = shortName; this.headsign = headsign; this.replacedTrips = replacedTrips; + this.dataSource = dataSource; } Result build() { if (calls.size() < 2) { - return UpdateError.result(tripId, TOO_FEW_STOPS); + return UpdateError.result(tripId, TOO_FEW_STOPS, dataSource); } if (serviceDate == null) { - return UpdateError.result(tripId, NO_START_DATE); + return UpdateError.result(tripId, NO_START_DATE, dataSource); } FeedScopedId calServiceId = transitService.getOrCreateServiceIdForDate(serviceDate); if (calServiceId == null) { - return UpdateError.result(tripId, NO_START_DATE); + return UpdateError.result(tripId, NO_START_DATE, dataSource); } boolean isAddedRoute = false; @@ -176,7 +182,7 @@ Result build() { if (route == null) { Agency agency = resolveAgency(); if (agency == null) { - return UpdateError.result(tripId, CANNOT_RESOLVE_AGENCY); + return UpdateError.result(tripId, CANNOT_RESOLVE_AGENCY, dataSource); } route = createRoute(agency); isAddedRoute = true; @@ -201,7 +207,7 @@ Result build() { // Drop this update if the call refers to an unknown stop (not present in the site repository). if (stopTime == null) { - return UpdateError.result(tripId, NO_VALID_STOPS); + return UpdateError.result(tripId, NO_VALID_STOPS, dataSource); } aimedStopTimes.add(stopTime); @@ -256,7 +262,7 @@ Result build() { try { updatedTripTimes.validateNonIncreasingTimes(); } catch (DataValidationException e) { - return DataValidationExceptionMapper.toResult(e); + return DataValidationExceptionMapper.toResult(e, dataSource); } var tripOnServiceDate = TripOnServiceDate @@ -273,7 +279,8 @@ Result build() { serviceDate, tripOnServiceDate, pattern, - isAddedRoute + isAddedRoute, + dataSource ) ); } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/ModifiedTripBuilder.java b/application/src/main/java/org/opentripplanner/updater/siri/ModifiedTripBuilder.java index 0f1475f7b50..06768087c74 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/ModifiedTripBuilder.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/ModifiedTripBuilder.java @@ -45,6 +45,7 @@ public class ModifiedTripBuilder { private final boolean cancellation; private final OccupancyEnumeration occupancy; private final boolean predictionInaccurate; + private final String dataSource; public ModifiedTripBuilder( TripTimes existingTripTimes, @@ -64,6 +65,7 @@ public ModifiedTripBuilder( cancellation = TRUE.equals(journey.isCancellation()); predictionInaccurate = TRUE.equals(journey.isPredictionInaccurate()); occupancy = journey.getOccupancy(); + dataSource = journey.getDataSource(); } /** @@ -78,7 +80,8 @@ public ModifiedTripBuilder( List calls, boolean cancellation, OccupancyEnumeration occupancy, - boolean predictionInaccurate + boolean predictionInaccurate, + String dataSource ) { this.existingTripTimes = existingTripTimes; this.pattern = pattern; @@ -89,6 +92,7 @@ public ModifiedTripBuilder( this.cancellation = cancellation; this.occupancy = occupancy; this.predictionInaccurate = predictionInaccurate; + this.dataSource = dataSource; } /** @@ -103,7 +107,9 @@ public Result build() { if (cancellation || stopPattern.isAllStopsNonRoutable()) { LOG.debug("Trip is cancelled"); newTimes.cancelTrip(); - return Result.success(new TripUpdate(pattern.getStopPattern(), newTimes, serviceDate)); + return Result.success( + new TripUpdate(pattern.getStopPattern(), newTimes, serviceDate, dataSource) + ); } applyUpdates(newTimes); @@ -116,7 +122,7 @@ public Result build() { newTimes.setRealTimeState(RealTimeState.MODIFIED); } - // TODO - Handle DataValidationException at the outemost level(pr trip) + // TODO - Handle DataValidationException at the outermost level (pr trip) try { newTimes.validateNonIncreasingTimes(); } catch (DataValidationException e) { @@ -125,7 +131,7 @@ public Result build() { newTimes.getTrip().getId(), e.getMessage() ); - return DataValidationExceptionMapper.toResult(e); + return DataValidationExceptionMapper.toResult(e, dataSource); } int numStopsInUpdate = newTimes.getNumStops(); @@ -137,11 +143,11 @@ public Result build() { numStopsInUpdate, numStopsInPattern ); - return UpdateError.result(existingTripTimes.getTrip().getId(), TOO_FEW_STOPS); + return UpdateError.result(existingTripTimes.getTrip().getId(), TOO_FEW_STOPS, dataSource); } LOG.debug("A valid TripUpdate object was applied using the Timetable class update method."); - return Result.success(new TripUpdate(stopPattern, newTimes, serviceDate)); + return Result.success(new TripUpdate(stopPattern, newTimes, serviceDate, dataSource)); } /** diff --git a/application/src/main/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandler.java b/application/src/main/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandler.java index a5817eca41b..9656d1a0b01 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandler.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandler.java @@ -135,9 +135,9 @@ private TransitAlert mapSituationToAlert( TransitAlertBuilder alert = createAlertWithTexts(situation); if ( - (alert.headerText() == null || alert.headerText().toString().isEmpty()) && - (alert.descriptionText() == null || alert.descriptionText().toString().isEmpty()) && - (alert.detailText() == null || alert.detailText().toString().isEmpty()) + I18NString.hasNoValue(alert.headerText()) && + I18NString.hasNoValue(alert.descriptionText()) && + I18NString.hasNoValue(alert.detailText()) ) { LOG.debug( "Empty Alert - ignoring situationNumber: {}", @@ -221,18 +221,18 @@ private long getEpochSecond(ZonedDateTime startTime) { private TransitAlertBuilder createAlertWithTexts(PtSituationElement situation) { return TransitAlert .of(new FeedScopedId(feedId, situation.getSituationNumber().getValue())) - .withDescriptionText(getTranslatedString(situation.getDescriptions())) - .withDetailText(getTranslatedString(situation.getDetails())) - .withAdviceText(getTranslatedString(situation.getAdvices())) - .withHeaderText(getTranslatedString(situation.getSummaries())) - .withUrl(getInfoLinkAsString(situation.getInfoLinks())) - .addSiriUrls(getInfoLinks(situation.getInfoLinks())); + .withDescriptionText(mapTranslatedString(situation.getDescriptions())) + .withDetailText(mapTranslatedString(situation.getDetails())) + .withAdviceText(mapTranslatedString(situation.getAdvices())) + .withHeaderText(mapTranslatedString(situation.getSummaries())) + .withUrl(mapInfoLinkToI18NString(situation.getInfoLinks())) + .addSiriUrls(mapInfoLinks(situation)); } /* * Returns first InfoLink-uri as a String */ - private I18NString getInfoLinkAsString(PtSituationElement.InfoLinks infoLinks) { + private I18NString mapInfoLinkToI18NString(PtSituationElement.InfoLinks infoLinks) { if (infoLinks != null) { if (isNotEmpty(infoLinks.getInfoLinks())) { InfoLinkStructure infoLinkStructure = infoLinks.getInfoLinks().get(0); @@ -247,21 +247,32 @@ private I18NString getInfoLinkAsString(PtSituationElement.InfoLinks infoLinks) { /* * Returns all InfoLinks */ - private List getInfoLinks(PtSituationElement.InfoLinks infoLinks) { + private List mapInfoLinks(PtSituationElement situation) { + PtSituationElement.InfoLinks infoLinks = situation.getInfoLinks(); List alertUrls = new ArrayList<>(); if (infoLinks != null) { if (isNotEmpty(infoLinks.getInfoLinks())) { for (InfoLinkStructure infoLink : infoLinks.getInfoLinks()) { - AlertUrl alertUrl = new AlertUrl(); - + String label = null; List labels = infoLink.getLabels(); if (labels != null && !labels.isEmpty()) { - NaturalLanguageStringStructure label = labels.get(0); - alertUrl.label = label.getValue(); + NaturalLanguageStringStructure lbl = labels.get(0); + label = lbl.getValue(); } - alertUrl.uri = infoLink.getUri(); - alertUrls.add(alertUrl); + var uri = infoLink.getUri(); + if (uri != null) { + alertUrls.add(new AlertUrl(uri, label)); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug( + "URI missing in info-link - ignoring info-link in situation: {}", + situation.getSituationNumber() != null + ? situation.getSituationNumber().getValue() + : null + ); + } + } } } } @@ -281,7 +292,7 @@ private boolean isNotEmpty(List list) { * * @return A TranslatedString containing the same information as the input */ - private I18NString getTranslatedString(List input) { + private I18NString mapTranslatedString(List input) { Map translations = new HashMap<>(); if (input != null && input.size() > 0) { for (DefaultedTextStructure textStructure : input) { diff --git a/application/src/main/java/org/opentripplanner/updater/siri/SiriTimetableSnapshotSource.java b/application/src/main/java/org/opentripplanner/updater/siri/SiriTimetableSnapshotSource.java index b6aa5310d83..fd716e7c232 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/SiriTimetableSnapshotSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/SiriTimetableSnapshotSource.java @@ -1,6 +1,7 @@ package org.opentripplanner.updater.siri; import static java.lang.Boolean.TRUE; +import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.EMPTY_STOP_POINT_REF; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NOT_MONITORED; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_FUZZY_TRIP_MATCH; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_START_DATE; @@ -33,6 +34,7 @@ import org.opentripplanner.updater.spi.UpdateSuccess; import org.opentripplanner.updater.trip.TimetableSnapshotManager; import org.opentripplanner.updater.trip.UpdateIncrementality; +import org.opentripplanner.utils.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -150,6 +152,11 @@ private Result apply( @Nullable SiriFuzzyTripMatcher fuzzyTripMatcher, EntityResolver entityResolver ) { + for (var call : CallWrapper.of(journey)) { + if (StringUtils.hasNoValueOrNullAsString(call.getStopPointRef())) { + return UpdateError.result(null, EMPTY_STOP_POINT_REF, journey.getDataSource()); + } + } boolean shouldAddNewTrip = false; try { shouldAddNewTrip = shouldAddNewTrip(journey, entityResolver); @@ -174,7 +181,7 @@ private Result apply( /* commit */ return addTripToGraphAndBuffer(result.successValue()); } catch (DataValidationException e) { - return DataValidationExceptionMapper.toResult(e); + return DataValidationExceptionMapper.toResult(e, journey.getDataSource()); } catch (Exception e) { LOG.warn( "{} EstimatedJourney {} failed.", @@ -217,6 +224,7 @@ private Result handleModifiedTrip( EstimatedVehicleJourney estimatedVehicleJourney ) { Trip trip = entityResolver.resolveTrip(estimatedVehicleJourney); + String dataSource = estimatedVehicleJourney.getDataSource(); // Check if EstimatedVehicleJourney is reported as NOT monitored, ignore the notMonitored-flag // if the journey is NOT monitored because it has been cancelled @@ -224,13 +232,13 @@ private Result handleModifiedTrip( !TRUE.equals(estimatedVehicleJourney.isMonitored()) && !TRUE.equals(estimatedVehicleJourney.isCancellation()) ) { - return UpdateError.result(trip != null ? trip.getId() : null, NOT_MONITORED); + return UpdateError.result(trip != null ? trip.getId() : null, NOT_MONITORED, dataSource); } LocalDate serviceDate = entityResolver.resolveServiceDate(estimatedVehicleJourney); if (serviceDate == null) { - return UpdateError.result(trip != null ? trip.getId() : null, NO_START_DATE); + return UpdateError.result(trip != null ? trip.getId() : null, NO_START_DATE, dataSource); } TripPattern pattern; @@ -252,20 +260,20 @@ private Result handleModifiedTrip( "No trips found for EstimatedVehicleJourney. {}", DebugString.of(estimatedVehicleJourney) ); - return UpdateError.result(null, NO_FUZZY_TRIP_MATCH); + return UpdateError.result(null, NO_FUZZY_TRIP_MATCH, dataSource); } trip = tripAndPattern.trip(); pattern = tripAndPattern.tripPattern(); } else { - return UpdateError.result(null, TRIP_NOT_FOUND); + return UpdateError.result(null, TRIP_NOT_FOUND, dataSource); } Timetable currentTimetable = getCurrentTimetable(pattern, serviceDate); TripTimes existingTripTimes = currentTimetable.getTripTimes(trip); if (existingTripTimes == null) { LOG.debug("tripId {} not found in pattern.", trip.getId()); - return UpdateError.result(trip.getId(), TRIP_NOT_FOUND_IN_PATTERN); + return UpdateError.result(trip.getId(), TRIP_NOT_FOUND_IN_PATTERN, dataSource); } var updateResult = new ModifiedTripBuilder( existingTripTimes, @@ -315,7 +323,8 @@ private Result addTripToGraphAndBuffer(TripUpdate tr serviceDate, tripUpdate.addedTripOnServiceDate(), tripUpdate.tripCreation(), - tripUpdate.routeCreation() + tripUpdate.routeCreation(), + tripUpdate.dataSource() ); var result = snapshotManager.updateBuffer(realTimeTripUpdate); LOG.debug("Applied real-time data for trip {} on {}", trip, serviceDate); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/TripUpdate.java b/application/src/main/java/org/opentripplanner/updater/siri/TripUpdate.java index 71c521b76fb..8432ed06d54 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/TripUpdate.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/TripUpdate.java @@ -11,12 +11,18 @@ /** * Represents the SIRI real-time update of a single trip. - * @param stopPattern the stop pattern to which belongs the updated trip. - * @param tripTimes the new trip times for the updated trip. - * @param serviceDate the service date for which this update applies (updates are valid only for one service date) - * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the TripOnServiceDate corresponding to this new trip. - * @param addedTripPattern optionally if this trip update adds a new trip pattern , the new trip pattern to this new trip. - * @param routeCreation true if an added trip cannot be registered under an existing route and a new route must be created. + * + * @param stopPattern the stop pattern to which belongs the updated trip. + * @param tripTimes the new trip times for the updated trip. + * @param serviceDate the service date for which this update applies (updates are valid + * only for one service date) + * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the + * TripOnServiceDate corresponding to this new trip. + * @param addedTripPattern optionally if this trip update adds a new trip pattern , the new + * trip pattern to this new trip. + * @param routeCreation true if an added trip cannot be registered under an existing route + * and a new route must be created. + * @param dataSource the dataSource of the real-time update. */ record TripUpdate( StopPattern stopPattern, @@ -24,7 +30,8 @@ record TripUpdate( LocalDate serviceDate, @Nullable TripOnServiceDate addedTripOnServiceDate, @Nullable TripPattern addedTripPattern, - boolean routeCreation + boolean routeCreation, + @Nullable String dataSource ) { public TripUpdate { Objects.requireNonNull(stopPattern); @@ -38,9 +45,10 @@ record TripUpdate( public TripUpdate( StopPattern stopPattern, RealTimeTripTimes updatedTripTimes, - LocalDate serviceDate + LocalDate serviceDate, + String dataSource ) { - this(stopPattern, updatedTripTimes, serviceDate, null, null, false); + this(stopPattern, updatedTripTimes, serviceDate, null, null, false, dataSource); } /** diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index 8bbe66559c6..dc479c034e1 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -15,7 +15,8 @@ public record SiriETUpdaterParameters( Duration timeout, Duration previewInterval, boolean fuzzyTripMatching, - HttpHeaders httpRequestHeaders + HttpHeaders httpRequestHeaders, + boolean producerMetrics ) implements PollingGraphUpdaterParameters, UrlUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java index 2970ac114c0..d7fb064966a 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java @@ -15,7 +15,8 @@ public record SiriETGooglePubsubUpdaterParameters( @Nullable String dataInitializationUrl, Duration reconnectPeriod, Duration initialGetDataTimeout, - boolean fuzzyTripMatching + boolean fuzzyTripMatching, + boolean producerMetrics ) implements UrlUpdaterParameters { public static Duration RECONNECT_PERIOD = Duration.ofSeconds(30); diff --git a/application/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java b/application/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java index dee103bc385..2a650f684f9 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/DataValidationExceptionMapper.java @@ -1,5 +1,6 @@ package org.opentripplanner.updater.spi; +import com.beust.jcommander.internal.Nullable; import org.opentripplanner.transit.model.framework.DataValidationException; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.timetable.TimetableValidationError; @@ -15,13 +16,20 @@ public class DataValidationExceptionMapper { private static final Logger LOG = LoggerFactory.getLogger(DataValidationExceptionMapper.class); public static Result toResult(DataValidationException error) { + return toResult(error, null); + } + + public static Result toResult( + DataValidationException error, + @Nullable String producer + ) { if (error.error() instanceof TimetableValidationError tt) { return Result.failure( - new UpdateError(tt.trip().getId(), mapTimeTableError(tt.code()), tt.stopIndex()) + new UpdateError(tt.trip().getId(), mapTimeTableError(tt.code()), tt.stopIndex(), producer) ); } // The mapper should handle all possible errors - LOG.error("Unhandled error: " + error.getMessage(), error); + LOG.error("Unhandled error: {}", error.getMessage(), error); return Result.failure(UpdateError.noTripId(UpdateError.UpdateErrorType.UNKNOWN)); } diff --git a/application/src/main/java/org/opentripplanner/updater/spi/UpdateError.java b/application/src/main/java/org/opentripplanner/updater/spi/UpdateError.java index 1f568ba99a4..945139e2457 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/UpdateError.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/UpdateError.java @@ -11,10 +11,18 @@ public record UpdateError( @Nullable FeedScopedId tripId, UpdateErrorType errorType, - @Nullable Integer stopIndex + @Nullable Integer stopIndex, + @Nullable String producer ) { public UpdateError(@Nullable FeedScopedId tripId, UpdateErrorType errorType) { - this(tripId, errorType, null); + this(tripId, errorType, null, null); + } + + public UpdateError(@Nullable FeedScopedId tripId, UpdateErrorType errorType, Integer stopIndex) { + this(tripId, errorType, stopIndex, null); + } + public UpdateError(@Nullable FeedScopedId tripId, UpdateErrorType errorType, String producer) { + this(tripId, errorType, null, producer); } public String debugId() { @@ -33,6 +41,7 @@ public enum UpdateErrorType { TRIP_NOT_FOUND, TRIP_NOT_FOUND_IN_PATTERN, NO_FUZZY_TRIP_MATCH, + EMPTY_STOP_POINT_REF, NO_TRIP_FOR_CANCELLATION_FOUND, TRIP_ALREADY_EXISTS, NO_START_DATE, @@ -56,6 +65,14 @@ public static Result result(FeedScopedId tripId, UpdateError return Result.failure(new UpdateError(tripId, errorType)); } + public static Result result( + FeedScopedId tripId, + UpdateErrorType errorType, + String producer + ) { + return Result.failure(new UpdateError(tripId, errorType, producer)); + } + public static UpdateError noTripId(UpdateErrorType errorType) { return new UpdateError(null, errorType); } diff --git a/application/src/main/java/org/opentripplanner/updater/spi/UpdateResult.java b/application/src/main/java/org/opentripplanner/updater/spi/UpdateResult.java index 643703f2252..0e47d8d202f 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/UpdateResult.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/UpdateResult.java @@ -1,6 +1,7 @@ package org.opentripplanner.updater.spi; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import java.util.List; @@ -15,23 +16,43 @@ public record UpdateResult( int successful, int failed, Multimap failures, - List warnings + List warnings, + List successes, + List errors ) { /** * Create an empty result. */ public static UpdateResult empty() { - return new UpdateResult(0, 0, ArrayListMultimap.create(), List.of()); + return new UpdateResult(0, 0, ArrayListMultimap.create(), List.of(), List.of(), List.of()); } /** * Aggregate a list of results into an instance of {@link UpdateResult}. */ public static UpdateResult ofResults(List> results) { - var errors = results.stream().filter(Result::isFailure).map(Result::failureValue).toList(); - var successes = results.stream().filter(Result::isSuccess).map(Result::successValue).toList(); - var warnings = successes.stream().flatMap(s -> s.warnings().stream()).toList(); - var errorIndex = Multimaps.index(errors, UpdateError::errorType); - return new UpdateResult(successes.size(), errors.size(), errorIndex, warnings); + List errors = results + .stream() + .filter(Result::isFailure) + .map(Result::failureValue) + .toList(); + List successes = results + .stream() + .filter(Result::isSuccess) + .map(Result::successValue) + .toList(); + List warnings = successes + .stream() + .flatMap(s -> s.warnings().stream()) + .toList(); + ImmutableListMultimap errorIndex = Multimaps.index(errors, UpdateError::errorType); + return new UpdateResult( + successes.size(), + errors.size(), + errorIndex, + warnings, + successes, + errors + ); } } diff --git a/application/src/main/java/org/opentripplanner/updater/spi/UpdateSuccess.java b/application/src/main/java/org/opentripplanner/updater/spi/UpdateSuccess.java index 6d797a9a650..861f4c5edff 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/UpdateSuccess.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/UpdateSuccess.java @@ -1,34 +1,35 @@ package org.opentripplanner.updater.spi; -import java.util.Arrays; import java.util.Collection; import java.util.List; import org.opentripplanner.utils.collection.ListUtils; /** * The result of a successful application of a realtime update, for example for trips or - * vehicle positions. Its only extra information is a collection of possible warnings that - * ought to be looked at but didn't prevent the application of the update. + * vehicle positions. Its extra information is a collection of possible warnings that + * ought to be looked at but didn't prevent the application of the update and the provider of the + * update. */ -public record UpdateSuccess(List warnings) { +public record UpdateSuccess(List warnings, String producer) { /** - * Create an instance with no warnings. + * Create an instance with no warnings and no provider. */ public static UpdateSuccess noWarnings() { - return new UpdateSuccess(List.of()); + return new UpdateSuccess(List.of(), null); } + /** - * Create an instance with the provided warnings. + * Create an instance with no warnings, but a provider. */ - public static UpdateSuccess ofWarnings(WarningType... warnings) { - return new UpdateSuccess(Arrays.asList(warnings)); + public static UpdateSuccess noWarnings(String producer) { + return new UpdateSuccess(List.of(), producer); } /** * Return a copy of the instance with the provided warnings added. */ public UpdateSuccess addWarnings(Collection addedWarnings) { - return new UpdateSuccess(ListUtils.combine(this.warnings, addedWarnings)); + return new UpdateSuccess(ListUtils.combine(this.warnings, addedWarnings), this.producer); } public enum WarningType { diff --git a/application/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/application/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 54c3901f3e8..4bc911e114c 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -35,6 +35,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Supplier; +import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.gtfs.mapping.TransitModeMapper; @@ -72,6 +73,7 @@ import org.opentripplanner.utils.time.ServiceDateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; /** * This class should be used to create snapshots of lookup tables of realtime data. This is @@ -153,17 +155,12 @@ public TimetableSnapshotSource( * @param updates GTFS-RT TripUpdate's that should be applied atomically */ public UpdateResult applyTripUpdates( - GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher, + @Nullable GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher, BackwardsDelayPropagationType backwardsDelayPropagationType, UpdateIncrementality updateIncrementality, List updates, String feedId ) { - if (updates == null) { - LOG.warn("updates is null"); - return UpdateResult.empty(); - } - Map failuresByRelationship = new HashMap<>(); List> results = new ArrayList<>(); @@ -172,11 +169,11 @@ public UpdateResult applyTripUpdates( snapshotManager.clearBuffer(feedId); } - LOG.debug("message contains {} trip updates", updates.size()); + debug(feedId, "message contains {} trip updates", updates.size()); int uIndex = 0; for (TripUpdate tripUpdate : updates) { if (!tripUpdate.hasTrip()) { - debug(feedId, "", "Missing TripDescriptor in gtfs-rt trip update: \n{}", tripUpdate); + debug(feedId, "Missing TripDescriptor in gtfs-rt trip update: \n{}", tripUpdate); continue; } @@ -188,7 +185,7 @@ public UpdateResult applyTripUpdates( final TripDescriptor tripDescriptor = tripUpdate.getTrip(); if (!tripDescriptor.hasTripId() || tripDescriptor.getTripId().isBlank()) { - debug(feedId, "", "No trip id found for gtfs-rt trip update: \n{}", tripUpdate); + debug(feedId, "No trip id found for gtfs-rt trip update: \n{}", tripUpdate); results.add(Result.failure(UpdateError.noTripId(INVALID_INPUT_STRUCTURE))); continue; } @@ -202,6 +199,7 @@ public UpdateResult applyTripUpdates( } catch (final ParseException e) { debug( tripId, + null, "Failed to parse start date in gtfs-rt trip update: {}", tripDescriptor.getStartDate() ); @@ -222,8 +220,24 @@ public UpdateResult applyTripUpdates( } uIndex += 1; - LOG.debug("trip update #{} ({} updates) :", uIndex, tripUpdate.getStopTimeUpdateCount()); - LOG.trace("{}", tripUpdate); + if (LOG.isTraceEnabled()) { + trace( + tripId, + serviceDate, + "trip update #{} ({} updates): {}", + uIndex, + tripUpdate.getStopTimeUpdateCount(), + tripUpdate + ); + } else { + debug( + tripId, + serviceDate, + "trip update #{} ({} updates)", + uIndex, + tripUpdate.getStopTimeUpdateCount() + ); + } Result result; try { @@ -268,8 +282,7 @@ public UpdateResult applyTripUpdates( results.add(result); if (result.isFailure()) { - debug(tripId, "Failed to apply TripUpdate."); - LOG.trace(" Contents: {}", tripUpdate); + debug(tripId, serviceDate, "Failed to apply TripUpdate."); if (failuresByRelationship.containsKey(scheduleRelationship)) { var c = failuresByRelationship.get(scheduleRelationship); failuresByRelationship.put(scheduleRelationship, ++c); @@ -357,7 +370,7 @@ private static void logUpdateResult( ResultLogger.logUpdateResult(feedId, "gtfs-rt-trip-updates", updateResult); if (!failuresByRelationship.isEmpty()) { - LOG.info("[feedId: {}] Failures by scheduleRelationship {}", feedId, failuresByRelationship); + info(feedId, "Failures by scheduleRelationship {}", failuresByRelationship); } var warnings = Multimaps.index(updateResult.warnings(), w -> w); @@ -365,7 +378,7 @@ private static void logUpdateResult( .keySet() .forEach(key -> { var count = warnings.get(key).size(); - LOG.info("[feedId: {}] {} warnings of type {}", feedId, count, key); + info(feedId, "{} warnings of type {}", count, key); }); } @@ -378,12 +391,12 @@ private Result handleScheduledTrip( final TripPattern pattern = getPatternForTripId(tripId); if (pattern == null) { - debug(tripId, "No pattern found for tripId, skipping TripUpdate."); + debug(tripId, serviceDate, "No pattern found for tripId, skipping TripUpdate."); return UpdateError.result(tripId, TRIP_NOT_FOUND); } if (tripUpdate.getStopTimeUpdateCount() < 1) { - debug(tripId, "TripUpdate contains no updates, skipping."); + debug(tripId, serviceDate, "TripUpdate contains no updates, skipping."); return UpdateError.result(tripId, NO_UPDATES); } @@ -394,6 +407,7 @@ private Result handleScheduledTrip( if (!serviceDates.contains(serviceDate)) { debug( tripId, + serviceDate, "SCHEDULED trip has service date {} for which trip's service is not valid, skipping.", serviceDate.toString() ); @@ -478,18 +492,26 @@ private Result validateAndHandleAddedTrip( if (trip != null) { // TODO: should we support this and add a new instantiation of this trip (making it // frequency based)? - debug(tripId, "Graph already contains trip id of ADDED trip, skipping."); + debug(tripId, serviceDate, "Graph already contains trip id of ADDED trip, skipping."); return UpdateError.result(tripId, TRIP_ALREADY_EXISTS); } // Check whether a start date exists if (!tripDescriptor.hasStartDate()) { // TODO: should we support this and apply update to all days? - debug(tripId, "ADDED trip doesn't have a start date in TripDescriptor, skipping."); + debug( + tripId, + serviceDate, + "ADDED trip doesn't have a start date in TripDescriptor, skipping." + ); return UpdateError.result(tripId, NO_START_DATE); } - final List stopTimeUpdates = removeUnknownStops(tripUpdate, tripId); + final List stopTimeUpdates = removeUnknownStops( + tripUpdate, + tripId, + serviceDate + ); var warnings = new ArrayList(0); @@ -499,12 +521,12 @@ private Result validateAndHandleAddedTrip( // check if after filtering the stops we still have at least 2 if (stopTimeUpdates.size() < 2) { - debug(tripId, "ADDED trip has fewer than two known stops, skipping."); + debug(tripId, serviceDate, "ADDED trip has fewer than two known stops, skipping."); return UpdateError.result(tripId, TOO_FEW_STOPS); } // Check whether all stop times are available and all stops exist - final var stops = checkNewStopTimeUpdatesAndFindStops(tripId, stopTimeUpdates); + final var stops = checkNewStopTimeUpdatesAndFindStops(tripId, serviceDate, stopTimeUpdates); if (stops == null) { return UpdateError.result(tripId, NO_VALID_STOPS); } @@ -519,7 +541,11 @@ private Result validateAndHandleAddedTrip( /** * Remove any stop that is not know in the static transit data. */ - private List removeUnknownStops(TripUpdate tripUpdate, FeedScopedId tripId) { + private List removeUnknownStops( + TripUpdate tripUpdate, + FeedScopedId tripId, + LocalDate serviceDate + ) { return tripUpdate .getStopTimeUpdateList() .stream() @@ -528,7 +554,12 @@ private List removeUnknownStops(TripUpdate tripUpdate, FeedScope var stopId = new FeedScopedId(tripId.getFeedId(), st.getStopId()); var stopFound = transitEditorService.getRegularStop(stopId) != null; if (!stopFound) { - debug(tripId, "Stop '{}' not found in graph. Removing from ADDED trip.", st.getStopId()); + debug( + tripId, + serviceDate, + "Stop '{}' not found in graph. Removing from ADDED trip.", + st.getStopId() + ); } return stopFound; }) @@ -543,6 +574,7 @@ private List removeUnknownStops(TripUpdate tripUpdate, FeedScope */ private List checkNewStopTimeUpdatesAndFindStops( final FeedScopedId tripId, + LocalDate serviceDate, final List stopTimeUpdates ) { Integer previousStopSequence = null; @@ -558,13 +590,13 @@ private List checkNewStopTimeUpdatesAndFindStops( // Check non-negative if (stopSequence < 0) { - debug(tripId, "Trip update contains negative stop sequence, skipping."); + debug(tripId, serviceDate, "Trip update contains negative stop sequence, skipping."); return null; } // Check whether sequence is increasing if (previousStopSequence != null && previousStopSequence > stopSequence) { - debug(tripId, "Trip update contains decreasing stop sequence, skipping."); + debug(tripId, serviceDate, "Trip update contains decreasing stop sequence, skipping."); return null; } previousStopSequence = stopSequence; @@ -584,13 +616,19 @@ private List checkNewStopTimeUpdatesAndFindStops( } else { debug( tripId, + serviceDate, "Graph doesn't contain stop id '{}' of trip update, skipping.", stopTimeUpdate.getStopId() ); return null; } } else { - debug(tripId, "Trip update misses a stop id at stop time list index {}, skipping.", index); + debug( + tripId, + serviceDate, + "Trip update misses a stop id at stop time list index {}, skipping.", + index + ); return null; } @@ -599,12 +637,12 @@ private List checkNewStopTimeUpdatesAndFindStops( // Check for increasing time final Long time = stopTimeUpdate.getArrival().getTime(); if (previousTime != null && previousTime > time) { - debug(tripId, "Trip update contains decreasing times, skipping."); + debug(tripId, serviceDate, "Trip update contains decreasing times, skipping."); return null; } previousTime = time; } else { - debug(tripId, "Trip update misses arrival time, skipping."); + debug(tripId, serviceDate, "Trip update misses arrival time, skipping."); return null; } @@ -613,12 +651,12 @@ private List checkNewStopTimeUpdatesAndFindStops( // Check for increasing time final Long time = stopTimeUpdate.getDeparture().getTime(); if (previousTime != null && previousTime > time) { - debug(tripId, "Trip update contains decreasing times, skipping."); + debug(tripId, serviceDate, "Trip update contains decreasing times, skipping."); return null; } previousTime = time; } else { - debug(tripId, "Trip update misses departure time, skipping."); + debug(tripId, serviceDate, "Trip update misses departure time, skipping."); return null; } } @@ -674,6 +712,7 @@ private Result handleAddedTrip( // No service id exists: return error for now debug( tripId, + serviceDate, "ADDED trip has service date {} for which no service id is available, skipping.", serviceDate.toString() ); @@ -804,6 +843,7 @@ private Result addTripToGraphAndBuffer( if (arrivalTime < 0 || arrivalTime > MAX_ARRIVAL_DEPARTURE_TIME) { debug( trip.getId(), + serviceDate, "ADDED trip has invalid arrival time (compared to start date in " + "TripDescriptor), skipping." ); @@ -818,6 +858,7 @@ private Result addTripToGraphAndBuffer( if (departureTime < 0 || departureTime > MAX_ARRIVAL_DEPARTURE_TIME) { debug( trip.getId(), + serviceDate, "ADDED trip has invalid departure time (compared to start date in " + "TripDescriptor), skipping." ); @@ -880,7 +921,9 @@ private Result addTripToGraphAndBuffer( .ifPresent(newTripTimes::updateWheelchairAccessibility); } } - LOG.trace( + trace( + trip.getId(), + serviceDate, "Trip pattern added with mode {} on {} from {} to {}", trip.getRoute().getMode(), serviceDate, @@ -920,7 +963,11 @@ private boolean cancelScheduledTrip( final Timetable timetable = pattern.getScheduledTimetable(); final int tripIndex = timetable.getTripIndex(tripId); if (tripIndex == -1) { - debug(tripId, "Could not cancel scheduled trip because it's not in the timetable"); + debug( + tripId, + serviceDate, + "Could not cancel scheduled trip because it's not in the timetable" + ); } else { final RealTimeTripTimes newTripTimes = timetable .getTripTimes(tripIndex) @@ -962,7 +1009,7 @@ private boolean cancelPreviouslyAddedTrip( final Timetable timetable = snapshotManager.resolve(pattern, serviceDate); final int tripIndex = timetable.getTripIndex(tripId); if (tripIndex == -1) { - debug(tripId, "Could not cancel previously added trip on {}", serviceDate); + debug(tripId, serviceDate, "Could not cancel previously added trip on {}", serviceDate); } else { final RealTimeTripTimes newTripTimes = timetable .getTripTimes(tripIndex) @@ -1004,14 +1051,18 @@ private Result validateAndHandleModifiedTrip( if (trip == null) { // TODO: should we support this and consider it an ADDED trip? - debug(tripId, "Feed does not contain trip id of MODIFIED trip, skipping."); + debug(tripId, serviceDate, "Feed does not contain trip id of MODIFIED trip, skipping."); return UpdateError.result(tripId, TRIP_NOT_FOUND); } // Check whether a start date exists if (!tripDescriptor.hasStartDate()) { // TODO: should we support this and apply update to all days? - debug(tripId, "REPLACEMENT trip doesn't have a start date in TripDescriptor, skipping."); + debug( + tripId, + serviceDate, + "REPLACEMENT trip doesn't have a start date in TripDescriptor, skipping." + ); return UpdateError.result(tripId, NO_START_DATE); } else { // Check whether service date is served by trip @@ -1020,19 +1071,27 @@ private Result validateAndHandleModifiedTrip( .getServiceIdsOnDate(serviceDate); if (!serviceIds.contains(trip.getServiceId())) { // TODO: should we support this and change service id of trip? - debug(tripId, "REPLACEMENT trip has a service date that is not served by trip, skipping."); + debug( + tripId, + serviceDate, + "REPLACEMENT trip has a service date that is not served by trip, skipping." + ); return UpdateError.result(tripId, NO_SERVICE_ON_DATE); } } // Check whether at least two stop updates exist if (tripUpdate.getStopTimeUpdateCount() < 2) { - debug(tripId, "REPLACEMENT trip has less then two stops, skipping."); + debug(tripId, serviceDate, "REPLACEMENT trip has less then two stops, skipping."); return UpdateError.result(tripId, TOO_FEW_STOPS); } // Check whether all stop times are available and all stops exist - var stops = checkNewStopTimeUpdatesAndFindStops(tripId, tripUpdate.getStopTimeUpdateList()); + var stops = checkNewStopTimeUpdatesAndFindStops( + tripId, + serviceDate, + tripUpdate.getStopTimeUpdateList() + ); if (stops == null) { return UpdateError.result(tripId, NO_VALID_STOPS); } @@ -1104,9 +1163,12 @@ private Result handleCanceledTrip( ); if (!cancelScheduledSuccess) { - debug(tripId, "No pattern found for tripId. Skipping cancellation."); + debug(tripId, serviceDate, "No pattern found for tripId. Skipping cancellation."); return UpdateError.result(tripId, NO_TRIP_FOR_CANCELLATION_FOUND); } + + debug(tripId, serviceDate, "Canceled trip"); + return Result.success(UpdateSuccess.noWarnings()); } @@ -1121,13 +1183,58 @@ private TripPattern getPatternForTripId(FeedScopedId tripId) { return transitEditorService.getPatternForTrip(trip); } - private static void debug(FeedScopedId id, String message, Object... params) { - debug(id.getFeedId(), id.getId(), message, params); + private static void debug( + FeedScopedId id, + @Nullable LocalDate serviceDate, + String message, + Object... params + ) { + log(Level.DEBUG, id.getFeedId(), id.getId(), serviceDate, message, params); + } + + private static void debug(String feedId, String message, Object... params) { + log(Level.DEBUG, feedId, null, null, message, params); } - private static void debug(String feedId, String tripId, String message, Object... params) { - String m = "[feedId: %s, tripId: %s] %s".formatted(feedId, tripId, message); - LOG.debug(m, params); + private static void trace( + FeedScopedId id, + @Nullable LocalDate serviceDate, + String message, + Object... params + ) { + log(Level.TRACE, id.getFeedId(), id.getId(), serviceDate, message, params); + } + + private static void info(String feedId, String message, Object... params) { + log(Level.INFO, feedId, null, null, message, params); + } + + /** + * This adds detailed per-update logging to allow tracking what feeds and updates were applied to + * a given trip. + *

+ * The INFO level is used for aggregated statistics, while DEBUG/TRACE is used to link specific + * messages to a trip. + */ + private static void log( + Level logLevel, + String feedId, + @Nullable String tripId, + @Nullable LocalDate serviceDate, + String message, + Object... params + ) { + if (LOG.isEnabledForLevel(logLevel)) { + String m = tripId != null || serviceDate != null + ? "[feedId: %s, tripId: %s, serviceDate: %s] %s".formatted( + feedId, + tripId, + serviceDate, + message + ) + : "[feedId: %s] %s".formatted(feedId, message); + LOG.makeLoggingEventBuilder(logLevel).log(m, params); + } } private enum CancelationType { diff --git a/application/src/main/java/org/opentripplanner/updater/trip/UrlUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/trip/UrlUpdaterParameters.java index 2070aedbbd6..4918be84e45 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/UrlUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/UrlUpdaterParameters.java @@ -4,4 +4,8 @@ public interface UrlUpdaterParameters { String url(); String configRef(); String feedId(); + + default boolean producerMetrics() { + return false; + } } diff --git a/application/src/main/java/org/opentripplanner/updater/trip/metrics/StreamingTripUpdateMetrics.java b/application/src/main/java/org/opentripplanner/updater/trip/metrics/StreamingTripUpdateMetrics.java index c53a398f3dc..0660f8d801b 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/metrics/StreamingTripUpdateMetrics.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/metrics/StreamingTripUpdateMetrics.java @@ -5,9 +5,7 @@ import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.List; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.UpdateSuccess; @@ -25,66 +23,58 @@ public class StreamingTripUpdateMetrics extends TripUpdateMetrics { protected static final String METRICS_PREFIX = "streaming_trip_updates"; - private final Counter successfulCounter; - private final Counter failureCounter; - private final Counter warningsCounter; - private final Map failuresByType = new HashMap<>(); - private final Map warningsByType = new HashMap<>(); + private final boolean producerMetrics; public StreamingTripUpdateMetrics(UrlUpdaterParameters parameters) { super(parameters); - this.successfulCounter = getCounter("successful", "Total successfully applied trip updates"); - this.failureCounter = getCounter("failed", "Total failed trip updates"); - this.warningsCounter = getCounter("warnings", "Total warnings for successful trip updates"); + this.producerMetrics = parameters.producerMetrics(); } public void setCounters(UpdateResult result) { - this.successfulCounter.increment(result.successful()); - this.failureCounter.increment(result.failed()); - this.warningsCounter.increment(result.warnings().size()); - - setFailures(result); - setWarnings(result); + incrementWarningCounts(result); + incrementFailureCounts(result); + incrementSuccessCounts(result); } - private void setWarnings(UpdateResult result) { + private void incrementWarningCounts(UpdateResult result) { for (var warningType : result.warnings()) { - var counter = warningsByType.get(warningType); - if (Objects.isNull(counter)) { - counter = - getCounter( - "warning_type", - "Total warnings by type generated by successful trip updates", - Tag.of("warningType", warningType.name()) - ); - warningsByType.put(warningType, counter); - } - counter.increment(); + Tags tags = Tags.concat(baseTags, Tags.of("warningType", warningType.name())); + Counter + .builder(METRICS_PREFIX + "." + "warnings") + .description("Total warnings by type generated by successful trip updates") + .tags(tags) + .register(Metrics.globalRegistry) + .increment(); } } - private void setFailures(UpdateResult result) { - for (var errorType : result.failures().keySet()) { - var counter = failuresByType.get(errorType); - if (Objects.isNull(counter)) { - counter = - getCounter( - "failure_type", - "Total failed trip updates by type", - Tag.of("errorType", errorType.name()) - ); - failuresByType.put(errorType, counter); + private void incrementFailureCounts(UpdateResult result) { + for (UpdateError error : result.errors()) { + Tags tags = Tags.concat(baseTags, Tags.of("errorType", error.errorType().name())); + if (producerMetrics && error.producer() != null) { + tags = tags.and(Tag.of("producer", error.producer())); } - counter.increment(result.failures().get(errorType).size()); + Counter + .builder(METRICS_PREFIX + "." + "failed") + .description("Total failed trip updates") + .tags(tags) + .register(Metrics.globalRegistry) + .increment(); } } - private Counter getCounter(String name, String description, Tag... tags) { - var finalTags = Tags.concat(Arrays.stream(tags).toList(), baseTags); - return Counter - .builder(METRICS_PREFIX + "." + name) - .description(description) - .tags(finalTags) - .register(Metrics.globalRegistry); + private void incrementSuccessCounts(UpdateResult result) { + for (UpdateSuccess success : result.successes()) { + Tags tags = Tags.of(baseTags); + if (producerMetrics && success.producer() != null) { + tags = tags.and(Tag.of("producer", success.producer())); + } + Counter + .builder(METRICS_PREFIX + "." + "successful") + .description("Total successfully applied trip updates") + .tags(tags) + .register(Metrics.globalRegistry) + .increment(); + } } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java index e3e86981bc8..331c4a15a70 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java @@ -22,6 +22,7 @@ import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,43 +76,44 @@ public List getUpdates() { final Map vehicleTypes = getVehicleTypes(system); List stations = new LinkedList<>(); - - // Both station information and status are required for all systems using stations - GBFSStationInformation stationInformation = loader.getFeed(GBFSStationInformation.class); - GBFSStationStatus stationStatus = loader.getFeed(GBFSStationStatus.class); - if (stationInformation != null && stationStatus != null) { - // Index all the station status entries on their station ID. - Map statusLookup = stationStatus - .getData() - .getStations() - .stream() - .collect(Collectors.toMap(GBFSStation::getStationId, Function.identity())); - GbfsStationStatusMapper stationStatusMapper = new GbfsStationStatusMapper( - statusLookup, - vehicleTypes - ); - GbfsStationInformationMapper stationInformationMapper = new GbfsStationInformationMapper( - system, - vehicleTypes, - params.allowKeepingRentedVehicleAtDestination(), - params.overloadingAllowed() - ); - - // Iterate over all known stations, and if we have any status information add it to those station objects. - stations.addAll( - stationInformation + if (params.allowRentalType(RentalPickupType.STATION)) { + // Both station information and status are required for all systems using stations + GBFSStationInformation stationInformation = loader.getFeed(GBFSStationInformation.class); + GBFSStationStatus stationStatus = loader.getFeed(GBFSStationStatus.class); + if (stationInformation != null && stationStatus != null) { + // Index all the station status entries on their station ID. + Map statusLookup = stationStatus .getData() .getStations() .stream() - .map(stationInformationMapper::mapStationInformation) - .filter(Objects::nonNull) - .peek(stationStatusMapper::fillStationStatus) - .toList() - ); + .collect(Collectors.toMap(GBFSStation::getStationId, Function.identity())); + GbfsStationStatusMapper stationStatusMapper = new GbfsStationStatusMapper( + statusLookup, + vehicleTypes + ); + GbfsStationInformationMapper stationInformationMapper = new GbfsStationInformationMapper( + system, + vehicleTypes, + params.allowKeepingRentedVehicleAtDestination(), + params.overloadingAllowed() + ); + + // Iterate over all known stations, and if we have any status information add it to those station objects. + stations.addAll( + stationInformation + .getData() + .getStations() + .stream() + .map(stationInformationMapper::mapStationInformation) + .filter(Objects::nonNull) + .peek(stationStatusMapper::fillStationStatus) + .toList() + ); + } } // Append the floating bike stations. - if (OTPFeature.FloatingBike.isOn()) { + if (OTPFeature.FloatingBike.isOn() && params.allowRentalType(RentalPickupType.FREE_FLOATING)) { GBFSFreeBikeStatus freeBikeStatus = loader.getFeed(GBFSFreeBikeStatus.class); if (freeBikeStatus != null) { GbfsFreeVehicleStatusMapper freeVehicleStatusMapper = new GbfsFreeVehicleStatusMapper( diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java index 7d7a6c75a5d..31c81079046 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java @@ -1,5 +1,7 @@ package org.opentripplanner.updater.vehicle_rental.datasources.params; +import java.util.Objects; +import java.util.Set; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; @@ -10,11 +12,20 @@ public record GbfsVehicleRentalDataSourceParameters( HttpHeaders httpHeaders, String network, boolean geofencingZones, - boolean overloadingAllowed + boolean overloadingAllowed, + Set rentalPickupTypes ) implements VehicleRentalDataSourceParameters { + public GbfsVehicleRentalDataSourceParameters { + Objects.requireNonNull(rentalPickupTypes); + } @Override public VehicleRentalSourceType sourceType() { return VehicleRentalSourceType.GBFS; } + + @Override + public boolean allowRentalType(RentalPickupType rentalPickupType) { + return rentalPickupTypes.contains(rentalPickupType); + } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/RentalPickupType.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/RentalPickupType.java new file mode 100644 index 00000000000..e117f59d9c1 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/RentalPickupType.java @@ -0,0 +1,38 @@ +package org.opentripplanner.updater.vehicle_rental.datasources.params; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; +import org.opentripplanner.framework.doc.DocumentedEnum; + +/** + * This is temporary and will be removed in a future version of OTP. + * + * Enum to specify the type of rental data that is allowed to be read from the data source. + */ +public enum RentalPickupType implements DocumentedEnum { + STATION("Stations are imported."), + FREE_FLOATING("Free-floating vehicles are imported."); + + public static final Set ALL = Collections.unmodifiableSet( + EnumSet.allOf(RentalPickupType.class) + ); + + private final String description; + + RentalPickupType(String description) { + this.description = description.stripIndent().trim(); + } + + @Override + public String typeDescription() { + return ( + "This is temporary and will be removed in a future version of OTP. Use this to specify the type of rental data that is allowed to be read from the data source." + ); + } + + @Override + public String enumValueDescription() { + return description; + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java index cdff994ab18..e87e7909bc5 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java @@ -13,4 +13,6 @@ public interface VehicleRentalDataSourceParameters { VehicleRentalSourceType sourceType(); HttpHeaders httpHeaders(); + + boolean allowRentalType(RentalPickupType rentalPickupType); } diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index e3871a5ed6c..a131b95fc8e 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,6 +73,9 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown +"Rental place union that represents either a VehicleRentalStation or a RentalVehicle" +union RentalPlace = RentalVehicle | VehicleRentalStation + union StopPosition = PositionAtStop | PositionBetweenStops "A public transport agency" @@ -117,23 +120,32 @@ type Alert implements Node { "Alert cause" alertCause: AlertCauseType "Long description of the alert" - alertDescriptionText: String! + alertDescriptionText( + "Returns description with the specified language, if found, otherwise returns the value with some default language." + language: String + ): String! "Long descriptions of the alert in all different available languages" - alertDescriptionTextTranslations: [TranslatedString!]! + alertDescriptionTextTranslations: [TranslatedString!]! @deprecated(reason : "Use `alertDescriptionText` instead. `accept-language` header can be used for translations or the `language` parameter on the `alertDescriptionText` field.") "Alert effect" alertEffect: AlertEffectType "hashcode from the original GTFS-RT alert" alertHash: Int "Header of the alert, if available" - alertHeaderText: String + alertHeaderText( + "Returns header with the specified language, if found, otherwise returns the value with some default language." + language: String + ): String "Header of the alert in all different available languages" - alertHeaderTextTranslations: [TranslatedString!]! + alertHeaderTextTranslations: [TranslatedString!]! @deprecated(reason : "Use `alertHeaderText` instead. `accept-language` header can be used for translations or the `language` parameter on the `alertHeaderText` field.") "Alert severity level" alertSeverityLevel: AlertSeverityLevelType "Url with more information" - alertUrl: String + alertUrl( + "Returns URL with the specified language, if found, otherwise returns the value with some default language." + language: String + ): String "Url with more information in all different available languages" - alertUrlTranslations: [TranslatedString!]! + alertUrlTranslations: [TranslatedString!]! @deprecated(reason : "Use `alertUrl` instead. `accept-language` header can be used for translations or the `language` parameter on the `alertUrl` field.") "Time when this alert is not in effect anymore. Format: Unix timestamp in seconds" effectiveEndDate: Long "Time when this alert comes into effect. Format: Unix timestamp in seconds" @@ -670,8 +682,10 @@ type Leg { """ headsign: String """ - An identifier for the leg, which can be used to re-fetch transit leg information. + An identifier for the leg, which can be used to re-fetch transit leg information, except leg's fare products. Re-fetching fails when the underlying transit data no longer exists. + **Note:** when both id and fare products are queried with [Relay](https://relay.dev/), id should be queried using a suitable GraphQL alias + such as `legId: id`. Relay does not accept different fare product ids in otherwise identical legs. """ id: String """ @@ -722,6 +736,24 @@ type Leg { pickupBookingInfo: BookingInfo "This is used to indicate if boarding this leg is possible only with special arrangements." pickupType: PickupDropoffType + "Previous legs with same origin and destination stops or stations" + previousLegs( + """ + Transportation modes for which all stops in the parent station are used as possible destination stops + for the previous legs. For modes not listed, only the exact destination stop of the leg is considered. + """ + destinationModesWithParentStation: [TransitMode!], + """ + The number of alternative legs searched. If fewer than the requested number are found, + then only the found legs are returned. + """ + numberOfLegs: Int!, + """ + Transportation modes for which all stops in the parent station are used as possible origin stops + for the previous legs. For modes not listed, only the exact origin stop of the leg is considered. + """ + originModesWithParentStation: [TransitMode!] + ): [Leg!] "Whether there is real-time data about this Leg" realTime: Boolean "State of real-time data" @@ -1173,6 +1205,7 @@ type QueryType { """ Try refetching the current state of a transit leg using its id. This fails when the underlying transit data (mostly IDs) has changed or are no longer available. + Fare products cannot be refetched using this query. """ leg(id: String!): Leg """ @@ -1483,6 +1516,11 @@ type QueryType { "List of routes and agencies which are given lower preference when planning the itinerary" unpreferred: InputUnpreferred, """ + The list of points the itinerary required to pass through. + All locations are visited in the order they are listed. + """ + via: [PlanViaLocationInput!], + """ How much less bad is waiting at the beginning of the trip (replaces `waitReluctance` on the first boarding). Default value: 0.4 """ @@ -1606,7 +1644,9 @@ type QueryType { is in combination of using paging can lead to better performance and to getting a more consistent number of itineraries in each search. """ - searchWindow: Duration + searchWindow: Duration, + "The list of points the itinerary is required to pass through." + via: [PlanViaLocationInput!] ): PlanConnection @async "Get a single rental vehicle based on its ID, i.e. value of field `vehicleId`" rentalVehicle(id: String!): RentalVehicle @@ -1731,6 +1771,17 @@ type QueryType { """ ids: [String] ): [VehicleRentalStation] + "Get all rental vehicles within the specified bounding box" + vehicleRentalsByBbox( + "Northern bound of the bounding box" + maximumLatitude: CoordinateValue!, + "Eastern bound of the bounding box" + maximumLongitude: CoordinateValue!, + "Southern bound of the bounding box" + minimumLatitude: CoordinateValue!, + "Western bound of the bounding box" + minimumLongitude: CoordinateValue! + ): [RentalPlace!]! "Needed until https://github.com/facebook/relay/issues/112 is resolved" viewer: QueryType } @@ -4098,6 +4149,20 @@ input PlanModesInput { transitOnly: Boolean = false } +""" +One of the listed stop locations must be visited on-board a transit vehicle or the journey must +alight or board at the location. +""" +input PlanPassThroughViaLocationInput { + "The label/name of the location. This is pass-through information and is not used in routing." + label: String + """ + A list of stop locations. A stop location can be a stop or a station. + It is enough to visit ONE of the locations listed. + """ + stopLocationIds: [String!]! +} + "Wrapper type for different types of preferences related to plan query." input PlanPreferencesInput { "Accessibility preferences that affect both the street and transit routing." @@ -4191,6 +4256,40 @@ input PlanTransitModesInput { transit: [PlanTransitModePreferenceInput!] } +""" +A via-location is used to specifying a location as an intermediate place the router must +route through. The via-location is either a pass-through-location or a visit-via-location. +""" +input PlanViaLocationInput @oneOf { + "Board, alight or pass-through(on-board) at the stop location." + passThrough: PlanPassThroughViaLocationInput + "Board or alight at a stop location or visit a coordinate." + visit: PlanVisitViaLocationInput +} + +""" +A visit-via-location is a physical visit to one of the stop locations or coordinates listed. An +on-board visit does not count, the traveler must alight or board at the given stop for it to to +be accepted. To visit a coordinate, the traveler must walk(bike or drive) to the closest point +in the street network from a stop and back to another stop to join the transit network. + +NOTE! Coordinates are NOT supported yet. +""" +input PlanVisitViaLocationInput { + "The label/name of the location. This is pass-through information and is not used in routing." + label: String + """ + The minimum wait time is used to force the trip to stay the given duration at the + via-location before the itinerary is continued. + """ + minimumWaitTime: Duration = "PT0S" + """ + A list of stop locations. A stop location can be a stop or a station. + It is enough to visit ONE of the locations listed. + """ + stopLocationIds: [String!] +} + "What criteria should be used when optimizing a scooter route." input ScooterOptimizationInput @oneOf { "Define optimization by weighing three criteria." diff --git a/application/src/test/java/org/opentripplanner/ConstantsForTests.java b/application/src/test/java/org/opentripplanner/ConstantsForTests.java index aeacc11eca4..f7877e62d6b 100644 --- a/application/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/application/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -15,6 +16,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.ConfiguredDataSource; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.module.DirectTransferGenerator; import org.opentripplanner.graph_builder.module.GtfsFeedId; import org.opentripplanner.graph_builder.module.TestStreetLinkerModule; import org.opentripplanner.graph_builder.module.ned.ElevationModule; @@ -27,6 +29,7 @@ import org.opentripplanner.netex.NetexBundle; import org.opentripplanner.netex.configure.NetexConfigure; import org.opentripplanner.osm.OsmProvider; +import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.fares.FareServiceFactory; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.LinkingDirection; @@ -167,7 +170,15 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) { addPortlandVehicleRentals(graph); - timetableRepository.index(); + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + Duration.ofMinutes(30), + List.of(new RouteRequest()) + ) + .buildGraph(); + graph.index(timetableRepository.getSiteRepository()); return new TestOtpModel(graph, timetableRepository); diff --git a/application/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java b/application/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java index 5a4526012c9..33569a34b2e 100644 --- a/application/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java +++ b/application/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java @@ -6,6 +6,8 @@ public class Coordinates { public static final Coordinate BERLIN = of(52.5212, 13.4105); public static final Coordinate BERLIN_BRANDENBURG_GATE = of(52.51627, 13.37770); + public static final Coordinate BERLIN_FERNSEHTURM = of(52.52084, 13.40934); + public static final Coordinate BERLIN_ADMIRALBRUCKE = of(52.49526, 13.415093); public static final Coordinate HAMBURG = of(53.5566, 10.0003); public static final Coordinate KONGSBERG_PLATFORM_1 = of(59.67216, 9.65107); public static final Coordinate BOSTON = of(42.36541, -71.06129); diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 3aad9075f73..88fbe943bba 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -117,7 +117,7 @@ class GraphQLIntegrationTest { .toList(); private static final Route ROUTE = TimetableRepositoryForTest.route("a-route").build(); - private static VehicleRentalStation VEHICLE_RENTAL_STATION = new TestVehicleRentalStationBuilder() + private static final VehicleRentalStation VEHICLE_RENTAL_STATION = new TestVehicleRentalStationBuilder() .withVehicles(10) .withSpaces(10) .withVehicleTypeBicycle(5, 7) @@ -125,7 +125,7 @@ class GraphQLIntegrationTest { .withSystem("Network-1", "https://foo.bar") .build(); - private static VehicleRentalVehicle RENTAL_VEHICLE = new TestFreeFloatingRentalVehicleBuilder() + private static final VehicleRentalVehicle RENTAL_VEHICLE = new TestFreeFloatingRentalVehicleBuilder() .withSystem("Network-1", "https://foo.bar") .build(); @@ -166,7 +166,7 @@ static void setup() { .trip("123") .withHeadsign(I18NString.of("Trip Headsign")) .build(); - var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, T11_00); + var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, "11:00"); var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, DEDUPLICATOR); final TripPattern pattern = TEST_MODEL .pattern(BUS) diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java index 3ca29f7c531..d81b5cf0d43 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java @@ -37,6 +37,8 @@ import org.opentripplanner.service.realtimevehicles.internal.DefaultRealtimeVehicleService; import org.opentripplanner.service.vehiclerental.internal.DefaultVehicleRentalService; import org.opentripplanner.street.search.TraverseMode; +import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TimetableRepository; @@ -46,7 +48,11 @@ class LegacyRouteRequestMapperTest implements PlanTestConstants { static { Graph graph = new Graph(); - var timetableRepository = new TimetableRepository(); + var testModel = TimetableRepositoryForTest.of(); + var stopModelBuilder = testModel + .siteRepositoryBuilder() + .withRegularStop(testModel.stop("stop1").build()); + var timetableRepository = new TimetableRepository(stopModelBuilder.build(), new Deduplicator()); timetableRepository.initTimeZone(ZoneIds.BERLIN); final DefaultTransitService transitService = new DefaultTransitService(timetableRepository); context = @@ -135,11 +141,15 @@ static Stream transportModesCases() { of(List.of(mode("BICYCLE")), "[ExcludeAllTransitFilter{}]"), of( List.of(mode("BUS")), + "[TransitFilterRequest{select: [SelectRequest{transportModes: [BUS]}]}]" + ), + of( + List.of(mode("BUS"), mode("COACH")), "[TransitFilterRequest{select: [SelectRequest{transportModes: [BUS, COACH]}]}]" ), of( List.of(mode("BUS"), mode("MONORAIL")), - "[TransitFilterRequest{select: [SelectRequest{transportModes: [BUS, COACH, MONORAIL]}]}]" + "[TransitFilterRequest{select: [SelectRequest{transportModes: [BUS, MONORAIL]}]}]" ) ); } @@ -242,7 +252,7 @@ void walkReluctance() { @Test void transferSlack() { - var seconds = 119L; + var seconds = 119; Map arguments = Map.of("minTransferTime", seconds); var routeRequest = LegacyRouteRequestMapper.toRouteRequest( @@ -255,6 +265,28 @@ void transferSlack() { assertEquals(TransferPreferences.DEFAULT.slack(), noParamsReq.preferences().transfer().slack()); } + @Test + void via() { + Map arguments = Map.of( + "via", + List.of( + Map.of("passThrough", Map.of("stopLocationIds", List.of("F:stop1"), "label", "a label")) + ) + ); + + var routeRequest = LegacyRouteRequestMapper.toRouteRequest( + executionContext(arguments), + context + ); + assertEquals( + "[PassThroughViaLocation{label: a label, stopLocationIds: [F:stop1]}]", + routeRequest.getViaLocations().toString() + ); + + var noParamsReq = LegacyRouteRequestMapper.toRouteRequest(executionContext(Map.of()), context); + assertEquals(List.of(), noParamsReq.getViaLocations()); + } + private DataFetchingEnvironment executionContext(Map arguments) { ExecutionInput executionInput = ExecutionInput .newExecutionInput() diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java index d4e48b63141..cf1dc759a3e 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java @@ -288,6 +288,32 @@ void testItineraryFilters() { assertEquals(multiplier, itinFilter.groupedOtherThanSameLegsMaxCostMultiplier()); } + @Test + void via() { + Map arguments = createArgsCopy(ARGS); + arguments.put( + "via", + List.of( + Map.of("passThrough", Map.of("stopLocationIds", List.of("F:stop1"), "label", "a label")) + ) + ); + + var routeRequest = RouteRequestMapper.toRouteRequest( + executionContext(arguments, LOCALE, CONTEXT), + CONTEXT + ); + assertEquals( + "[PassThroughViaLocation{label: a label, stopLocationIds: [F:stop1]}]", + routeRequest.getViaLocations().toString() + ); + + var noParamsReq = RouteRequestMapper.toRouteRequest( + executionContext(ARGS, LOCALE, CONTEXT), + CONTEXT + ); + assertEquals(List.of(), noParamsReq.getViaLocations()); + } + static Map createArgsCopy(Map arguments) { Map newArgs = new HashMap<>(); newArgs.putAll(arguments); diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapperTest.java new file mode 100644 index 00000000000..a3eb74a5d43 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ViaLocationMapperTest.java @@ -0,0 +1,103 @@ +package org.opentripplanner.apis.gtfs.mapping.routerequest; + +import static java.util.Map.entry; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.FIELD_LABEL; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.FIELD_MINIMUM_WAIT_TIME; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.FIELD_PASS_THROUGH; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.FIELD_STOP_LOCATION_IDS; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.FIELD_VISIT; +import static org.opentripplanner.apis.gtfs.mapping.routerequest.ViaLocationMapper.mapToViaLocations; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ViaLocationMapperTest { + + public static final String LABEL = "TestLabel"; + public static final Duration MIN_WAIT_TIME = Duration.ofMinutes(5); + public static final List LIST_IDS_INPUT = List.of("F:ID1", "F:ID2"); + public static final String EXPECTED_IDS_AS_STRING = "[F:ID1, F:ID2]"; + + @Test + void mapToVisitViaLocations() { + Map> args = Map.of( + FIELD_VISIT, + Map.ofEntries( + entry(FIELD_LABEL, LABEL), + entry(FIELD_MINIMUM_WAIT_TIME, MIN_WAIT_TIME), + entry(FIELD_STOP_LOCATION_IDS, LIST_IDS_INPUT) + ) + ); + + var inputs = List.of(args); + var result = mapToViaLocations(inputs); + + var via = result.getFirst(); + + assertEquals(LABEL, via.label()); + assertEquals(MIN_WAIT_TIME, via.minimumWaitTime()); + assertEquals(EXPECTED_IDS_AS_STRING, via.stopLocationIds().toString()); + assertFalse(via.isPassThroughLocation()); + assertEquals( + "[VisitViaLocation{label: TestLabel, minimumWaitTime: 5m, stopLocationIds: [F:ID1, F:ID2], coordinates: []}]", + result.toString() + ); + } + + @Test + void mapToVisitViaLocationsWithBareMinimum() { + Map> args = Map.of( + FIELD_VISIT, + Map.of(FIELD_STOP_LOCATION_IDS, List.of("F:1")) + ); + var inputs = List.of(args); + var result = mapToViaLocations(inputs); + + var via = result.getFirst(); + + assertNull(via.label()); + assertEquals(Duration.ZERO, via.minimumWaitTime()); + assertEquals("[F:1]", via.stopLocationIds().toString()); + assertFalse(via.isPassThroughLocation()); + } + + @Test + void mapToPassThrough() { + final Map> args = Map.of( + FIELD_PASS_THROUGH, + Map.ofEntries(entry(FIELD_LABEL, LABEL), entry(FIELD_STOP_LOCATION_IDS, LIST_IDS_INPUT)) + ); + var inputs = List.of(args); + var result = mapToViaLocations(inputs); + var via = result.getFirst(); + + assertEquals(LABEL, via.label()); + assertEquals(EXPECTED_IDS_AS_STRING, via.stopLocationIds().toString()); + assertTrue(via.isPassThroughLocation()); + assertEquals( + "PassThroughViaLocation{label: TestLabel, stopLocationIds: [F:ID1, F:ID2]}", + via.toString() + ); + } + + @Test + void mapToPassThroughWithBareMinimum() { + Map> args = Map.of( + FIELD_PASS_THROUGH, + Map.of(FIELD_STOP_LOCATION_IDS, List.of("F:1")) + ); + var inputs = List.of(args); + var result = mapToViaLocations(inputs); + var via = result.getFirst(); + + assertNull(via.label()); + assertEquals("[F:1]", via.stopLocationIds().toString()); + assertTrue(via.isPassThroughLocation()); + } +} diff --git a/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java b/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java deleted file mode 100644 index c190ff1abac..00000000000 --- a/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.opentripplanner.astar.strategy; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder; -import org.opentripplanner.street.search.state.TestStateBuilder; - -class MaxCountSkipEdgeStrategyTest { - - private final StreetNearbyStopFinder finder = new StreetNearbyStopFinder(null, 0, null); - - @Test - void countStops() { - var state = TestStateBuilder.ofWalking().stop().build(); - var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); - assertFalse(strategy.shouldSkipEdge(state, null)); - assertTrue(strategy.shouldSkipEdge(state, null)); - } - - @Test - void doNotCountStop() { - var state = TestStateBuilder.ofWalking().build(); - var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); - assertFalse(strategy.shouldSkipEdge(state, null)); - assertFalse(strategy.shouldSkipEdge(state, null)); - assertFalse(strategy.shouldSkipEdge(state, null)); - } - - @Test - void nonFinalState() { - var state = TestStateBuilder.ofScooterRentalArriveBy().stop().build(); - assertFalse(state.isFinal()); - var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); - assertFalse(strategy.shouldSkipEdge(state, null)); - } -} diff --git a/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategyTest.java b/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategyTest.java new file mode 100644 index 00000000000..50a920f1252 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/astar/strategy/MaxCountTerminationStrategyTest.java @@ -0,0 +1,29 @@ +package org.opentripplanner.astar.strategy; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class MaxCountTerminationStrategyTest { + + @Test + void countStates() { + var countAllStatesStrategy = new MaxCountTerminationStrategy<>(3, state -> true); + + assertFalse(countAllStatesStrategy.shouldSearchTerminate(null)); + assertFalse(countAllStatesStrategy.shouldSearchTerminate(null)); + assertTrue(countAllStatesStrategy.shouldSearchTerminate(null)); + assertTrue(countAllStatesStrategy.shouldSearchTerminate(null)); + } + + @Test + void countNoStates() { + var countNoStatesStrategy = new MaxCountTerminationStrategy<>(1, state -> false); + + assertFalse(countNoStatesStrategy.shouldSearchTerminate(null)); + assertFalse(countNoStatesStrategy.shouldSearchTerminate(null)); + assertFalse(countNoStatesStrategy.shouldSearchTerminate(null)); + assertFalse(countNoStatesStrategy.shouldSearchTerminate(null)); + } +} diff --git a/application/src/test/java/org/opentripplanner/framework/i18n/I18NStringTest.java b/application/src/test/java/org/opentripplanner/framework/i18n/I18NStringTest.java new file mode 100644 index 00000000000..101da6edc91 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/framework/i18n/I18NStringTest.java @@ -0,0 +1,32 @@ +package org.opentripplanner.framework.i18n; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class I18NStringTest { + + private final I18NString noValue = I18NString.of(" \t\n\r\f"); + private final I18NString hasValue = I18NString.of("A value"); + + @Test + void hasValue() { + assertTrue(I18NString.hasValue(hasValue)); + assertFalse(I18NString.hasValue(noValue)); + } + + @Test + void hasNoValue() { + assertFalse(I18NString.hasNoValue(hasValue)); + assertTrue(I18NString.hasNoValue(noValue)); + } + + @Test + void assertHasValue() { + var ex = assertThrows(IllegalArgumentException.class, () -> I18NString.assertHasValue(noValue)); + assertEquals("Value can not be null, empty or just whitespace: ' \t\n\r\f'", ex.getMessage()); + } +} diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderMultipleLinksTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderMultipleLinksTest.java new file mode 100644 index 00000000000..3466c1bfd8a --- /dev/null +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderMultipleLinksTest.java @@ -0,0 +1,71 @@ +package org.opentripplanner.graph_builder.module.nearbystops; + +import static com.google.common.truth.Truth.assertThat; +import static org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinderTest.assertStopAtDistance; +import static org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinderTest.assertZeroDistanceStop; +import static org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinderTest.sort; + +import java.time.Duration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.routing.algorithm.GraphRoutingTest; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.request.StreetRequest; +import org.opentripplanner.street.model.vertex.TransitStopVertex; + +class StreetNearbyStopFinderMultipleLinksTest extends GraphRoutingTest { + + private static final WgsCoordinate origin = new WgsCoordinate(0.0, 0.0); + private TransitStopVertex stopA; + private TransitStopVertex stopB; + private TransitStopVertex stopC; + + @BeforeEach + protected void setUp() throws Exception { + modelOf( + new Builder() { + @Override + public void build() { + var A = intersection("A", origin); + var B = intersection("B", origin.moveEastMeters(100)); + var C = intersection("C", origin.moveEastMeters(200)); + + biStreet(A, B, 100); + biStreet(B, C, 100); + + stopA = stop("StopA", A.toWgsCoordinate()); + stopB = stop("StopB", B.toWgsCoordinate()); + stopC = stop("StopC", C.toWgsCoordinate()); + + biLink(A, stopA); + + // B has many links + biLink(B, stopB); + biLink(B, stopB); + biLink(B, stopB); + biLink(B, stopB); + + biLink(C, stopC); + } + } + ); + } + + @Test + void testMaxStopCountRegression() { + // Max-stop-count should work correctly even though there are multiple links B <-> stopB + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 3; + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null); + + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(3); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(2)); + } +} diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java index 261a40454f0..aae02451b33 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java @@ -99,7 +99,6 @@ void testMultipleStops() { } @Test - @Disabled("Currently disabled because of a bug in stop counting") void testMaxStopCount() { var durationLimit = Duration.ofMinutes(10); var maxStopCount = 2; @@ -150,7 +149,6 @@ void testIgnoreStops() { } @Test - @Disabled("Currently disabled because of a bug in stop counting") void testIgnoreStopsWithMaxStops() { var durationLimit = Duration.ofMinutes(10); var maxStopCount = 1; @@ -165,14 +163,14 @@ void testIgnoreStopsWithMaxStops() { assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0)); } - private List sort(Collection stops) { + static List sort(Collection stops) { return stops.stream().sorted(Comparator.comparing(x -> x.distance)).toList(); } /** * Verify that the nearby stop is zero distance and corresponds to the expected vertex */ - private void assertZeroDistanceStop(TransitStopVertex expected, NearbyStop nearbyStop) { + static void assertZeroDistanceStop(TransitStopVertex expected, NearbyStop nearbyStop) { assertEquals(expected.getStop(), nearbyStop.stop); assertEquals(0, nearbyStop.distance); assertEquals(0, nearbyStop.edges.size()); @@ -183,7 +181,7 @@ private void assertZeroDistanceStop(TransitStopVertex expected, NearbyStop nearb /** * Verify that the nearby stop is at a specific distance and corresponds to the expected vertex */ - private void assertStopAtDistance( + static void assertStopAtDistance( TransitStopVertex expected, double expectedDistance, NearbyStop nearbyStop diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessorTest.java new file mode 100644 index 00000000000..bfe5992399b --- /dev/null +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/ParkingProcessorTest.java @@ -0,0 +1,60 @@ +package org.opentripplanner.graph_builder.module.osm; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.Coordinates; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.osm.wayproperty.specifier.WayTestData; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.street.model._data.StreetModelForTest; +import org.opentripplanner.street.model.vertex.IntersectionVertex; + +class ParkingProcessorTest { + + private static final IntersectionVertex INTERSECTION_VERTEX = StreetModelForTest.intersectionVertex( + 1, + 1 + ); + private static final ParkingProcessor PROCESSOR = new ParkingProcessor( + new Graph(), + DataImportIssueStore.NOOP, + (n, w) -> INTERSECTION_VERTEX + ); + + @Test + void noWheelchairParking() { + var entity = WayTestData.parkAndRide(); + var parking = PROCESSOR.createVehicleParkingObjectFromOsmEntity( + true, + Coordinates.BERLIN, + entity, + I18NString.of("parking"), + List.of() + ); + + assertFalse(parking.hasWheelchairAccessibleCarPlaces()); + assertNull(parking.getCapacity().getWheelchairAccessibleCarSpaces()); + } + + @Test + void wheelchairParking() { + var entity = WayTestData.parkAndRide(); + entity.addTag("capacity:disabled", "yes"); + var parking = PROCESSOR.createVehicleParkingObjectFromOsmEntity( + true, + Coordinates.BERLIN, + entity, + I18NString.of("parking"), + List.of() + ); + + assertTrue(parking.hasWheelchairAccessibleCarPlaces()); + assertEquals(1, parking.getCapacity().getWheelchairAccessibleCarSpaces()); + } +} diff --git a/application/src/test/java/org/opentripplanner/gtfs/mapping/BikeAccessMapperTest.java b/application/src/test/java/org/opentripplanner/gtfs/mapping/BikeAccessMapperTest.java index a17886311dc..d5ed02dfd9c 100644 --- a/application/src/test/java/org/opentripplanner/gtfs/mapping/BikeAccessMapperTest.java +++ b/application/src/test/java/org/opentripplanner/gtfs/mapping/BikeAccessMapperTest.java @@ -11,10 +11,6 @@ public class BikeAccessMapperTest { private static final int BIKES_ALLOWED = 1; private static final int BIKES_NOT_ALLOWED = 2; - private static final int TRIP_BIKES_ALLOWED = 2; - private static final int TRIP_BIKES_NOT_ALLOWED = 1; - private static final int ROUTE_BIKES_ALLOWED = 2; - private static final int ROUTE_BIKES_NOT_ALLOWED = 1; @Test public void testTripProvidedValues() { @@ -28,25 +24,12 @@ public void testTripProvidedValues() { assertEquals(BikeAccess.NOT_ALLOWED, BikeAccessMapper.mapForTrip(trip)); } - @Test - public void testLegacyTripProvidedValues() { - Trip trip = new Trip(); - assertEquals(BikeAccess.UNKNOWN, BikeAccessMapper.mapForTrip(trip)); - - trip.setTripBikesAllowed(TRIP_BIKES_ALLOWED); - assertEquals(BikeAccess.ALLOWED, BikeAccessMapper.mapForTrip(trip)); - - trip.setTripBikesAllowed(TRIP_BIKES_NOT_ALLOWED); - assertEquals(BikeAccess.NOT_ALLOWED, BikeAccessMapper.mapForTrip(trip)); - } - @Test public void testTripProvidedValuesPrecedence() { Trip trip = new Trip(); assertEquals(BikeAccess.UNKNOWN, BikeAccessMapper.mapForTrip(trip)); trip.setBikesAllowed(BIKES_ALLOWED); - trip.setTripBikesAllowed(TRIP_BIKES_NOT_ALLOWED); assertEquals(BikeAccess.ALLOWED, BikeAccessMapper.mapForTrip(trip)); } @@ -61,26 +44,4 @@ public void testRouteProvidedValues() { route.setBikesAllowed(BIKES_NOT_ALLOWED); assertEquals(BikeAccess.NOT_ALLOWED, BikeAccessMapper.mapForRoute(route)); } - - @Test - public void testLegacyRouteProvidedValues() { - Route route = new Route(); - assertEquals(BikeAccess.UNKNOWN, BikeAccessMapper.mapForRoute(route)); - - route.setRouteBikesAllowed(ROUTE_BIKES_ALLOWED); - assertEquals(BikeAccess.ALLOWED, BikeAccessMapper.mapForRoute(route)); - - route.setRouteBikesAllowed(ROUTE_BIKES_NOT_ALLOWED); - assertEquals(BikeAccess.NOT_ALLOWED, BikeAccessMapper.mapForRoute(route)); - } - - @Test - public void testRouteProvidedValuesPrecedence() { - Route route = new Route(); - assertEquals(BikeAccess.UNKNOWN, BikeAccessMapper.mapForRoute(route)); - - route.setBikesAllowed(BIKES_ALLOWED); - route.setRouteBikesAllowed(ROUTE_BIKES_NOT_ALLOWED); - assertEquals(BikeAccess.ALLOWED, BikeAccessMapper.mapForRoute(route)); - } } diff --git a/application/src/test/java/org/opentripplanner/gtfs/mapping/TransitModeMapperTest.java b/application/src/test/java/org/opentripplanner/gtfs/mapping/TransitModeMapperTest.java index a872b1456f4..55e6f3a0d86 100644 --- a/application/src/test/java/org/opentripplanner/gtfs/mapping/TransitModeMapperTest.java +++ b/application/src/test/java/org/opentripplanner/gtfs/mapping/TransitModeMapperTest.java @@ -5,6 +5,7 @@ import static org.opentripplanner.transit.model.basic.TransitMode.BUS; import static org.opentripplanner.transit.model.basic.TransitMode.CABLE_CAR; import static org.opentripplanner.transit.model.basic.TransitMode.CARPOOL; +import static org.opentripplanner.transit.model.basic.TransitMode.COACH; import static org.opentripplanner.transit.model.basic.TransitMode.FERRY; import static org.opentripplanner.transit.model.basic.TransitMode.FUNICULAR; import static org.opentripplanner.transit.model.basic.TransitMode.GONDOLA; @@ -42,6 +43,8 @@ static Stream testCases() { // https://groups.google.com/g/gtfs-changes/c/keT5rTPS7Y0/m/71uMz2l6ke0J?pli=1 Arguments.of(100, RAIL), Arguments.of(199, RAIL), + Arguments.of(200, COACH), + Arguments.of(299, COACH), Arguments.of(400, RAIL), Arguments.of(401, SUBWAY), Arguments.of(402, SUBWAY), diff --git a/application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java b/application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java index 97e34b134bf..7fc2b577fd2 100644 --- a/application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java +++ b/application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java @@ -17,7 +17,7 @@ void gtfsSequence() { var testModel = TimetableRepositoryForTest.of(); var pattern = testModel.pattern(TransitMode.BUS).build(); var trip = TimetableRepositoryForTest.trip("123").build(); - var stopTimes = testModel.stopTimesEvery5Minutes(3, trip, T11_00); + var stopTimes = testModel.stopTimesEvery5Minutes(3, trip, "11:00"); var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); diff --git a/application/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java b/application/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java index 556cfc1ca90..210d58e054e 100644 --- a/application/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java +++ b/application/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.calendar.CalendarServiceData; -import org.opentripplanner.model.plan.PlanTestConstants; import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -56,7 +55,7 @@ static void buildTransitService() { Trip trip = TimetableRepositoryForTest.trip("1").build(); var tripTimes = TripTimesFactory.tripTimes( trip, - TEST_MODEL.stopTimesEvery5Minutes(5, trip, PlanTestConstants.T11_00), + TEST_MODEL.stopTimesEvery5Minutes(5, trip, "11:00"), new Deduplicator() ); tripTimes.setServiceCode(SERVICE_CODE); diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index 2f81cc55e3f..a89f8612040 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -8,8 +8,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; public class OsmWithTagsTest { @@ -272,4 +276,26 @@ void fallbackName() { var namedTunnel = WayTestData.carTunnel(); assertFalse(namedTunnel.hasNoName()); } + + private static List parseIntOrBooleanCases() { + return List.of( + Arguments.of("true", OptionalInt.of(1)), + Arguments.of("yes", OptionalInt.of(1)), + Arguments.of("no", OptionalInt.of(0)), + Arguments.of("false", OptionalInt.of(0)), + Arguments.of("0", OptionalInt.of(0)), + Arguments.of("12", OptionalInt.of(12)), + Arguments.of("", OptionalInt.empty()) + ); + } + + @ParameterizedTest + @MethodSource("parseIntOrBooleanCases") + void parseIntOrBoolean(String value, OptionalInt expected) { + var way = new OsmWithTags(); + var key = "capacity:disabled"; + way.addTag(key, value); + var maybeInt = way.parseIntOrBoolean(key, i -> {}); + assertEquals(expected, maybeInt); + } } diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapperTest.java new file mode 100644 index 00000000000..6256044320d --- /dev/null +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/ConstantSpeedMapperTest.java @@ -0,0 +1,23 @@ +package org.opentripplanner.osm.tagmapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.osm.model.OsmWithTags; + +public class ConstantSpeedMapperTest { + + @Test + public void constantSpeedCarRouting() { + OsmTagMapper osmTagMapper = new ConstantSpeedFinlandMapper(20f); + + var slowWay = new OsmWithTags(); + slowWay.addTag("highway", "residential"); + assertEquals(20f, osmTagMapper.getCarSpeedForWay(slowWay, true)); + + var fastWay = new OsmWithTags(); + fastWay.addTag("highway", "motorway"); + fastWay.addTag("maxspeed", "120 kmph"); + assertEquals(20f, osmTagMapper.getCarSpeedForWay(fastWay, true)); + } +} diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/FinlandMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/FinlandMapperTest.java index e3f24f64c6c..f8b07e06bf6 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/FinlandMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/FinlandMapperTest.java @@ -1,8 +1,12 @@ package org.opentripplanner.osm.tagmapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.street.model.StreetTraversalPermission.NONE; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.model.OsmWay; import org.opentripplanner.osm.model.OsmWithTags; @@ -11,12 +15,15 @@ public class FinlandMapperTest { - static WayPropertySet wps = new WayPropertySet(); + private WayPropertySet wps; + private OsmTagMapper mapper; static float epsilon = 0.01f; - static { - var source = new FinlandMapper(); - source.populateProperties(wps); + @BeforeEach + public void setup() { + this.wps = new WayPropertySet(); + this.mapper = new FinlandMapper(); + this.mapper.populateProperties(this.wps); } /** @@ -194,4 +201,36 @@ public void testTagMapping() { wayData = wps.getDataForWay(way); assertEquals(wayData.getPermission(), NONE); } + + /** + * Test that biking is not allowed in footway areas and transit platforms + */ + @Test + public void testArea() { + OsmWithTags way; + WayProperties wayData; + + way = new OsmWay(); + way.addTag("highway", "footway"); + way.addTag("area", "yes"); + wayData = wps.getDataForWay(way); + assertEquals(wayData.getPermission(), PEDESTRIAN); + + way = new OsmWay(); + way.addTag("public_transport", "platform"); + way.addTag("area", "yes"); + wayData = wps.getDataForWay(way); + assertEquals(wayData.getPermission(), PEDESTRIAN); + way.addTag("bicycle", "yes"); + wayData = wps.getDataForWay(way); + assertEquals(wayData.getPermission(), PEDESTRIAN_AND_BICYCLE); + } + + @Test + public void serviceNoThroughTraffic() { + var way = new OsmWay(); + way.addTag("highway", "residential"); + way.addTag("service", "driveway"); + assertTrue(mapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(way)); + } } diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/GermanyMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/GermanyMapperTest.java index 28f482a3388..c0901f4fcf5 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/GermanyMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/GermanyMapperTest.java @@ -1,6 +1,8 @@ package org.opentripplanner.osm.tagmapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -279,4 +281,19 @@ void testGermanAutobahnSpeed() { autobahn.addTag("maxspeed", "none"); assertEquals(33.33000183105469, wps.getCarSpeedForWay(autobahn, false), epsilon); } + + /** + * Test that biking is not allowed in transit platforms + */ + @Test + public void testArea() { + OsmWithTags way; + + way = new OsmWithTags(); + way.addTag("public_transport", "platform"); + way.addTag("area", "yes"); + assertEquals(wps.getDataForWay(way).getPermission(), PEDESTRIAN); + way.addTag("bicycle", "yes"); + assertEquals(wps.getDataForWay(way).getPermission(), PEDESTRIAN_AND_BICYCLE); + } } diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java index e0c70690e91..b3ee57f9710 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java @@ -3,20 +3,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofBicycleSafety; -import static org.opentripplanner.osm.wayproperty.WayPropertiesBuilder.withModes; import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.model.OsmWithTags; -import org.opentripplanner.osm.wayproperty.WayPropertySet; public class OsmTagMapperTest { @Test public void isMotorThroughTrafficExplicitlyDisallowed() { OsmWithTags o = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); assertFalse(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(o)); @@ -37,172 +34,135 @@ public void isMotorThroughTrafficExplicitlyDisallowed() { } @Test - public void constantSpeedCarRouting() { - OsmTagMapper osmTagMapper = new ConstantSpeedFinlandMapper(20f); - - var slowWay = new OsmWithTags(); - slowWay.addTag("highway", "residential"); - assertEquals(20f, osmTagMapper.getCarSpeedForWay(slowWay, true)); - - var fastWay = new OsmWithTags(); - fastWay.addTag("highway", "motorway"); - fastWay.addTag("maxspeed", "120 kmph"); - assertEquals(20f, osmTagMapper.getCarSpeedForWay(fastWay, true)); - } - - @Test - public void isBicycleNoThroughTrafficExplicitlyDisallowed() { - OsmTagMapper osmTagMapper = new DefaultMapper(); - assertTrue( - osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(way("bicycle", "destination")) - ); + public void isBicycleThroughTrafficExplicitlyDisallowed() { + OsmTagMapper osmTagMapper = new OsmTagMapper(); assertTrue( - osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(way("access", "destination")) + osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(way("bicycle", "destination")) ); - } - - @Test - public void isWalkNoThroughTrafficExplicitlyDisallowed() { - OsmTagMapper osmTagMapper = new DefaultMapper(); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(way("foot", "destination"))); assertTrue( - osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(way("access", "destination")) + osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(way("access", "destination")) ); } @Test - public void mixin() { - var source = new DefaultMapper(); - var wps = new WayPropertySet(); - - wps.setProperties("tag=imaginary", withModes(CAR).bicycleSafety(2)); - - wps.setMixinProperties("foo=bar", ofBicycleSafety(0.5)); - source.populateProperties(wps); - - var withoutFoo = new OsmWithTags(); - withoutFoo.addTag("tag", "imaginary"); - assertEquals(2, wps.getDataForWay(withoutFoo).bicycleSafety().back()); - - // the mixin for foo=bar reduces the bike safety factor - var withFoo = new OsmWithTags(); - withFoo.addTag("tag", "imaginary"); - withFoo.addTag("foo", "bar"); - assertEquals(1, wps.getDataForWay(withFoo).bicycleSafety().back()); + public void isWalkThroughTrafficExplicitlyDisallowed() { + OsmTagMapper osmTagMapper = new OsmTagMapper(); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(way("foot", "destination"))); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(way("access", "destination"))); } @Test public void testAccessNo() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "no"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testAccessPrivate() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "private"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testFootModifier() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "private"); tags.addTag("foot", "yes"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testVehicleDenied() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("vehicle", "destination"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testVehicleDeniedMotorVehiclePermissive() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("vehicle", "destination"); tags.addTag("motor_vehicle", "designated"); assertFalse(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testVehicleDeniedBicyclePermissive() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("vehicle", "destination"); tags.addTag("bicycle", "designated"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testMotorcycleModifier() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "private"); tags.addTag("motor_vehicle", "yes"); assertFalse(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testBicycleModifier() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "private"); tags.addTag("bicycle", "yes"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } @Test public void testBicyclePermissive() { OsmWithTags tags = new OsmWithTags(); - OsmTagMapper osmTagMapper = new DefaultMapper(); + OsmTagMapper osmTagMapper = new OsmTagMapper(); tags.addTag("access", "private"); tags.addTag("bicycle", "permissive"); assertTrue(osmTagMapper.isMotorVehicleThroughTrafficExplicitlyDisallowed(tags)); - assertFalse(osmTagMapper.isBicycleNoThroughTrafficExplicitlyDisallowed(tags)); - assertTrue(osmTagMapper.isWalkNoThroughTrafficExplicitlyDisallowed(tags)); + assertFalse(osmTagMapper.isBicycleThroughTrafficExplicitlyDisallowed(tags)); + assertTrue(osmTagMapper.isWalkThroughTrafficExplicitlyDisallowed(tags)); } public OsmWithTags way(String key, String value) { diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/DefaultMapperTest.java b/application/src/test/java/org/opentripplanner/osm/wayproperty/MapperTest.java similarity index 89% rename from application/src/test/java/org/opentripplanner/osm/tagmapping/DefaultMapperTest.java rename to application/src/test/java/org/opentripplanner/osm/wayproperty/MapperTest.java index 87e23acbf12..2cd9a74f06b 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/DefaultMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/wayproperty/MapperTest.java @@ -1,21 +1,23 @@ -package org.opentripplanner.osm.tagmapping; +package org.opentripplanner.osm.wayproperty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.osm.wayproperty.MixinPropertiesBuilder.ofBicycleSafety; +import static org.opentripplanner.osm.wayproperty.WayPropertiesBuilder.withModes; import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; +import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.model.OsmWithTags; -import org.opentripplanner.osm.wayproperty.SpeedPicker; -import org.opentripplanner.osm.wayproperty.WayPropertySet; +import org.opentripplanner.osm.tagmapping.OsmTagMapper; import org.opentripplanner.osm.wayproperty.specifier.BestMatchSpecifier; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; -public class DefaultMapperTest { +public class MapperTest { private WayPropertySet wps; private OsmTagMapper mapper; @@ -24,7 +26,7 @@ public class DefaultMapperTest { @BeforeEach public void setup() { var wps = new WayPropertySet(); - DefaultMapper source = new DefaultMapper(); + var source = new OsmTagMapper(); source.populateProperties(wps); this.wps = wps; this.mapper = source; @@ -199,6 +201,22 @@ void slopeOverrides() { assertTrue(wps.getSlopeOverride(indoor)); } + @Test + public void mixin() { + wps.setProperties("tag=imaginary", withModes(CAR).bicycleSafety(2)); + wps.setMixinProperties("foo=bar", ofBicycleSafety(0.5)); + + var withoutFoo = new OsmWithTags(); + withoutFoo.addTag("tag", "imaginary"); + assertEquals(2, wps.getDataForWay(withoutFoo).bicycleSafety().back()); + + // the mixin for foo=bar reduces the bike safety factor + var withFoo = new OsmWithTags(); + withFoo.addTag("tag", "imaginary"); + withFoo.addTag("foo", "bar"); + assertEquals(1, wps.getDataForWay(withFoo).bicycleSafety().back()); + } + /** * Test that two values are within epsilon of each other. */ diff --git a/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java b/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java index e075955f6c4..6d468b42db0 100644 --- a/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java +++ b/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java @@ -224,4 +224,12 @@ public static OsmWithTags indoor(String value) { way.addTag("indoor", value); return way; } + + public static OsmWithTags parkAndRide() { + var way = new OsmWithTags(); + way.addTag("amenity", "parking"); + way.addTag("park_ride", "yes"); + way.addTag("capacity", "10"); + return way; + } } diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/D02_TransitModeReluctanceTest.java b/application/src/test/java/org/opentripplanner/raptor/moduletests/D02_TransitModeReluctanceTest.java deleted file mode 100644 index 970ae42a3ef..00000000000 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/D02_TransitModeReluctanceTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.opentripplanner.raptor.moduletests; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.raptor._data.api.PathUtils.pathsToString; -import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; - -import java.util.stream.Stream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor.RaptorService; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; -import org.opentripplanner.raptor.api.request.RaptorProfile; -import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.configure.RaptorConfig; -import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; -import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig; - -/** - * FEATURE UNDER TEST - *

- * Raptor should return transit option with the lowest cost when to rides are equal, but have - * different transit-reluctance. - */ -public class D02_TransitModeReluctanceTest implements RaptorTestConstants { - - public static final double[] PREFER_R1 = { 0.99, 1.0 }; - public static final double[] PREFER_R2 = { 0.9, 0.89 }; - private final TestTransitData data = new TestTransitData(); - private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); - private final RaptorService raptorService = new RaptorService<>( - RaptorConfig.defaultConfigForTest() - ); - - @BeforeEach - public void setup() { - // Given 2 identical routes R1 and R2 - data.withRoute( - route(pattern("R1", STOP_A, STOP_B)) - .withTimetable(schedule("00:01, 00:02:40").transitReluctanceIndex(0)) - ); - data.withRoute( - route(pattern("R2", STOP_A, STOP_B)) - .withTimetable(schedule("00:01, 00:02:40").transitReluctanceIndex(1)) - ); - - requestBuilder - .searchParams() - .addAccessPaths(TestAccessEgress.walk(STOP_A, D30s)) - .addEgressPaths(TestAccessEgress.walk(STOP_B, D20s)) - .earliestDepartureTime(T00_00) - .latestArrivalTime(T00_10) - .timetable(true); - - requestBuilder.profile(RaptorProfile.MULTI_CRITERIA); - - ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); - } - - static Stream testCases() { - return RaptorModuleTestConfig - .multiCriteria() - .build() - .stream() - .flatMap(config -> - Stream.of( - Arguments.of( - PREFER_R1, - config, - "Walk 30s ~ A ~ BUS R1 0:01 0:02:40 ~ B ~ Walk 20s " + "[0:00:30 0:03 2m30s Tₓ0 C₁799]" - ), - Arguments.of( - PREFER_R2, - config, - "Walk 30s ~ A ~ BUS R2 0:01 0:02:40 ~ B ~ Walk 20s " + "[0:00:30 0:03 2m30s Tₓ0 C₁789]" - ) - ) - ); - } - - @ParameterizedTest(name = "Transit reluctance [R1, R2]: {0}, profile: {1}") - @MethodSource("testCases") - void testTransitReluctance( - double[] transitReluctance, - RaptorModuleTestConfig testConfig, - String expected - ) { - data.mcCostParamsBuilder().transitReluctanceFactors(transitReluctance); - var request = testConfig.apply(requestBuilder).build(); - var response = raptorService.route(request, data); - assertEquals(expected, pathsToString(response)); - } -} diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/D03_UnpreferredRouteTest.java b/application/src/test/java/org/opentripplanner/raptor/moduletests/D03_UnpreferredRouteTest.java deleted file mode 100644 index 4a7aac19e01..00000000000 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/D03_UnpreferredRouteTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.opentripplanner.raptor.moduletests; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.raptor._data.api.PathUtils.pathsToString; -import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; - -import java.util.BitSet; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor.RaptorService; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; -import org.opentripplanner.raptor.api.request.RaptorProfile; -import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.configure.RaptorConfig; -import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; -import org.opentripplanner.routing.api.request.framework.CostLinearFunction; -import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; -import org.opentripplanner.transit.model.framework.FeedScopedId; - -/** - * FEATURE UNDER TEST - *

- * On transit options with identical cost, raptor should drop the unpreferred one which is modeled - * by route penalty. - */ -public class D03_UnpreferredRouteTest implements RaptorTestConstants { - - private static final String EXPECTED = - "Walk 30s ~ A ~ BUS %s 0:01 0:02:40 ~ B ~ Walk 20s " + "[0:00:30 0:03 2m30s Tₓ0 C₁%d]"; - private static final FeedScopedId ROUTE_ID_1 = TimetableRepositoryForTest.id("1"); - private static final FeedScopedId ROUTE_ID_2 = TimetableRepositoryForTest.id("2"); - private static final CostLinearFunction UNPREFERRED_C1 = CostLinearFunction.of("5m + 1t"); - private final TestTransitData data = new TestTransitData(); - private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); - private final RaptorService raptorService = new RaptorService<>( - RaptorConfig.defaultConfigForTest() - ); - - @BeforeEach - public void setup() { - // Given 2 identical routes R1 and R2 - data.withRoute( - route( - pattern("R1", STOP_A, STOP_B) - .withRoute(TimetableRepositoryForTest.route(ROUTE_ID_1).build()) - ) - .withTimetable(schedule("00:01, 00:02:40")) - ); - data.withRoute( - route( - pattern("R2", STOP_A, STOP_B) - .withRoute(TimetableRepositoryForTest.route(ROUTE_ID_2).build()) - ) - .withTimetable(schedule("00:01, 00:02:40")) - ); - - requestBuilder - .searchParams() - .addAccessPaths(TestAccessEgress.walk(STOP_A, D30s)) - .addEgressPaths(TestAccessEgress.walk(STOP_B, D20s)) - .earliestDepartureTime(T00_00) - .latestArrivalTime(T00_10) - .timetable(true); - - requestBuilder.profile(RaptorProfile.MULTI_CRITERIA); - - ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); - } - - @Test - public void unpreferR1() { - unpreferRoute(ROUTE_ID_1); - - var request = requestBuilder.build(); - var response = raptorService.route(request, data); - - // Verify R1 is preferred and the cost is correct - assertEquals(expected("R2", 800), pathsToString(response)); - } - - @Test - public void unpreferR2() { - unpreferRoute(ROUTE_ID_2); - - var request = requestBuilder.build(); - var response = raptorService.route(request, data); - - assertEquals(expected("R1", 800), pathsToString(response)); - } - - private void unpreferRoute(FeedScopedId routeId) { - final BitSet patterns = new BitSet(); - for (var pattern : data.getPatterns()) { - if (pattern.route().getId().equals(routeId)) { - patterns.set(pattern.patternIndex()); - } - } - data.mcCostParamsBuilder().unpreferredPatterns(patterns); - data.mcCostParamsBuilder().unpreferredCost(UNPREFERRED_C1); - } - - private static String expected(String route, int cost) { - return String.format(EXPECTED, route, cost); - } -} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/RaptorTestConstants.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/RaptorTestConstants.java new file mode 100644 index 00000000000..88a6b97bb75 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/RaptorTestConstants.java @@ -0,0 +1,92 @@ +package org.opentripplanner.raptorlegacy._data; + +import static org.opentripplanner.utils.time.DurationUtils.durationInSeconds; +import static org.opentripplanner.utils.time.TimeUtils.hm2time; + +import org.opentripplanner.raptor.spi.DefaultSlackProvider; +import org.opentripplanner.raptor.spi.RaptorSlackProvider; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public interface RaptorTestConstants { + // Time duration(D) constants, all values are in seconds + int D0s = 0; + int D1s = 1; + int D10s = 10; + int D11s = 11; + int D20s = 20; + int D30s = 30; + int D40s = 40; + int D1m = durationInSeconds("1m"); + int D2m = durationInSeconds("2m"); + int D3m = durationInSeconds("3m"); + int D4m = durationInSeconds("4m"); + int D5m = durationInSeconds("5m"); + int D7m = durationInSeconds("7m"); + int D8m = durationInSeconds("8m"); + int D10m = durationInSeconds("10m"); + int D11m = durationInSeconds("11m"); + int D20m = durationInSeconds("20m"); + int D24h = durationInSeconds("24h"); + + /** + * There are 86400 seconds in a "normal" day(24 * 60 * 60). + */ + int SECONDS_IN_A_DAY = (int) D24h; + + // Time constants, all values are in seconds + int T00_00 = hm2time(0, 0); + int T00_02 = hm2time(0, 2); + int T00_10 = hm2time(0, 10); + int T00_30 = hm2time(0, 30); + int T00_40 = hm2time(0, 40); + int T01_00 = hm2time(1, 0); + + int TX_0 = 0; + int TX_1 = 1; + int TX_2 = 2; + + // Stop indexes - Note! There is no stop defined for index 0(zero)! You must + // account for that in the test if you use the stop index. + int STOP_A = 1; + int STOP_B = 2; + int STOP_C = 3; + int STOP_D = 4; + int STOP_E = 5; + int STOP_F = 6; + int STOP_G = 7; + int STOP_H = 8; + int STOP_I = 9; + int STOP_J = 10; + int STOP_K = 11; + int STOP_L = 12; + int STOP_M = 13; + + int NUM_STOPS = 14; + + // Stop position in pattern + int STOP_POS_0 = 0; + int STOP_POS_1 = 1; + + // Slack + int BOARD_SLACK = 45; + int ALIGHT_SLACK = 15; + int TRANSFER_SLACK = 60; + + RaptorSlackProvider SLACK_PROVIDER = new DefaultSlackProvider( + TRANSFER_SLACK, + BOARD_SLACK, + ALIGHT_SLACK + ); + + // FLEX + int ONE_RIDE = 1; + int TWO_RIDES = 2; + + default String stopIndexToName(int index) { + return Character.toString('A' + index - 1); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/PathUtils.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/PathUtils.java new file mode 100644 index 00000000000..15e16217fa2 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/PathUtils.java @@ -0,0 +1,61 @@ +package org.opentripplanner.raptorlegacy._data.api; + +import java.util.Collection; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.opentripplanner.raptor.api.path.RaptorPath; +import org.opentripplanner.raptor.api.response.RaptorResponse; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; + +/** + * This utility help converting a Raptor path to a string which is used in several unit tests for + * easy comparison. The Stop index(1..n) is translated to stop names(A..N) using {@link + * RaptorTestConstants#stopIndexToName(int)}. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class PathUtils { + + private static final RaptorTestConstants TRANSLATOR = new RaptorTestConstants() {}; + + /** Util class, private constructor */ + private PathUtils() {} + + public static String pathsToString(RaptorResponse response) { + return pathsToString(response.paths()); + } + + public static String pathsToString(Collection> paths) { + return pathsToString(paths, p -> p.toString(TRANSLATOR::stopIndexToName)); + } + + public static String pathsToStringDetailed(RaptorResponse response) { + return pathsToStringDetailed(response.paths()); + } + + public static String pathsToStringDetailed(Collection> paths) { + return pathsToString(paths, p -> p.toStringDetailed(TRANSLATOR::stopIndexToName)); + } + + public static String join(String... paths) { + return String.join("\n", paths); + } + + public static String withoutCost(String path) { + return path.replaceAll(" C₁[\\d_]+", ""); + } + + public static String[] withoutCost(String... paths) { + return Stream.of(paths).map(path -> withoutCost(path)).toList().toArray(new String[0]); + } + + public static String pathsToString( + Collection> paths, + Function, String> mapToStr + ) { + return paths.stream().sorted().map(mapToStr).collect(Collectors.joining("\n")); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilder.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilder.java new file mode 100644 index 00000000000..a7fe2f9f3c8 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilder.java @@ -0,0 +1,157 @@ +package org.opentripplanner.raptorlegacy._data.api; + +import static org.opentripplanner.raptor.rangeraptor.transit.TripTimesSearch.findTripTimes; + +import javax.annotation.Nullable; +import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorStopNameResolver; +import org.opentripplanner.raptor.api.path.RaptorPath; +import org.opentripplanner.raptor.path.PathBuilder; +import org.opentripplanner.raptor.spi.DefaultSlackProvider; +import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptor.spi.RaptorSlackProvider; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; + +/** + * Utility to help build paths for testing. The path builder is "reusable", every time the {@code + * access(...)} methods are called the builder reset it self. + *

+ * If the {@code costCalculator} is null, paths will not include cost. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class TestPathBuilder implements RaptorTestConstants { + + private static final int BOARD_ALIGHT_OFFSET = 30; + + @Nullable + private final RaptorCostCalculator costCalculator; + + private final RaptorSlackProvider slackProvider; + private PathBuilder builder; + private int startTime; + private int c2 = RaptorConstants.NOT_SET; + + public TestPathBuilder( + RaptorSlackProvider slackProvider, + @Nullable RaptorCostCalculator costCalculator + ) { + this.slackProvider = slackProvider; + this.costCalculator = costCalculator; + } + + /** + * Uses the slacks in {@link RaptorTestConstants}. + */ + public TestPathBuilder(@Nullable RaptorCostCalculator costCalculator) { + this(new DefaultSlackProvider(TRANSFER_SLACK, BOARD_SLACK, ALIGHT_SLACK), costCalculator); + } + + /** Assign c2 value for path. TODO: Add c2 value for each leg. */ + public TestPathBuilder c2(int c2) { + this.c2 = c2; + return this; + } + + /** + * Create access starting at the fixed given {@code starting}. Opening hours is used to enforce + * the access start time and prevent time-shifting it. + */ + public TestPathBuilder access(int startTime, int toStop, int duration) { + return access(startTime, TestAccessEgress.walk(toStop, duration)); + } + + /** Same as {@link #access(int, int, int)} , but with a free access - duration is 0s. */ + public TestPathBuilder access(int startTime, int toStop) { + return access(startTime, TestAccessEgress.free(toStop)); + } + + /** + * Create access with the given {@code startTime}, but allow the access to be time-shifted + * according to the opening hours of the given {@code transfer}. + */ + private TestPathBuilder access(int startTime, TestAccessEgress transfer) { + reset(startTime); + builder.access(transfer); + return this; + } + + public TestPathBuilder walk(int duration, int toStop) { + return walk(TestTransfer.transfer(toStop, duration)); + } + + public TestPathBuilder walk(int duration, int toStop, int cost) { + return walk(TestTransfer.transfer(toStop, duration, cost)); + } + + public TestPathBuilder walk(TestTransfer transfer) { + builder.transfer(transfer, transfer.stop()); + return this; + } + + public TestPathBuilder bus(TestTripSchedule trip, int alightStop) { + int boardStop = currentStop(); + // We use the startTime as earliest-board-time, this may cause problems for + // testing routes visiting the same stop more than once. Create a new factory + // method if this happens. + var baTime = findTripTimes(trip, boardStop, alightStop, startTime); + builder.transit(trip, baTime); + return this; + } + + public TestPathBuilder bus(String patternName, int fromTime, int duration, int toStop) { + int toTime = fromTime + duration; + int fromStop = currentStop(); + + TestTripSchedule trip = TestTripSchedule + .schedule(TestTripPattern.pattern(patternName, fromStop, toStop)) + .arrDepOffset(BOARD_ALIGHT_OFFSET) + .departures(fromTime, toTime + BOARD_ALIGHT_OFFSET) + .build(); + + return bus(trip, toStop); + } + + public RaptorPath egress(int duration) { + return egress( + duration == 0 + ? TestAccessEgress.free(currentStop()) + : TestAccessEgress.walk(currentStop(), duration) + ); + } + + public PathBuilder access(TestAccessEgress access) { + builder.access(access); + return builder; + } + + public RaptorPath egress(TestAccessEgress egress) { + builder.egress(egress); + builder.c2(c2); + return builder.build(); + } + + /* private methods */ + + int currentStop() { + return builder.tail().toStop(); + } + + private void reset(int startTime) { + this.startTime = startTime; + this.builder = + PathBuilder.tailPathBuilder( + slackProvider, + startTime, + costCalculator, + RaptorStopNameResolver.nullSafe(null), + null + ); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilderTestRaptor.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilderTestRaptor.java new file mode 100644 index 00000000000..e68bf7ad7cf --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/api/TestPathBuilderTestRaptor.java @@ -0,0 +1,99 @@ +package org.opentripplanner.raptorlegacy._data.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.model.transfer.TransferConstraint.REGULAR_TRANSFER; +import static org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; +import static org.opentripplanner.utils.time.DurationUtils.durationInSeconds; +import static org.opentripplanner.utils.time.TimeUtils.time; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; + +/** + * Test the PathBuilder to be sure that it works properly before using it in other tests. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class TestPathBuilderTestRaptor implements RaptorTestConstants { + + private final TestPathBuilder subject = new TestPathBuilder(C1_CALCULATOR); + + @Test + public void testSimplePathWithOneTransit() { + int transitDuration = durationInSeconds("5m"); + + var path = subject + .access(time("10:00:15"), STOP_A, D1m) + .bus("L1", time("10:02"), transitDuration, STOP_B) + .egress(D2m); + + var transitLeg = path.accessLeg().nextLeg().asTransitLeg(); + int boardCost = C1_CALCULATOR.boardingCost( + true, + path.accessLeg().toTime(), + STOP_A, + transitLeg.fromTime(), + transitLeg.trip(), + REGULAR_TRANSFER + ); + + int transitCost = C1_CALCULATOR.transitArrivalCost( + boardCost, + ALIGHT_SLACK, + transitDuration, + BasicPathTestCase.TRIP_1, + STOP_B + ); + + int accessEgressCost = C1_CALCULATOR.costEgress(TestAccessEgress.walk(STOP_B, D2m + D1m)); + + assertEquals(accessEgressCost + transitCost, path.c1()); + assertEquals( + "Walk 1m 10:00:15 10:01:15 C₁120 ~ A 45s " + + "~ BUS L1 10:02 10:07 5m C₁438 ~ B 15s " + + "~ Walk 2m 10:07:15 10:09:15 C₁210 " + + "[10:00:15 10:09:15 9m Tₓ0 C₁768]", + path.toStringDetailed(this::stopIndexToName) + ); + } + + @Test + public void testBasicPath() { + var path = subject + .c2(7) + .access(BasicPathTestCase.ACCESS_START, STOP_A, BasicPathTestCase.ACCESS_DURATION) + .bus( + BasicPathTestCase.LINE_11, + BasicPathTestCase.L11_START, + BasicPathTestCase.L11_DURATION, + STOP_B + ) + .walk(BasicPathTestCase.TX_DURATION, STOP_C, BasicPathTestCase.TX_C1) + .bus( + BasicPathTestCase.LINE_21, + BasicPathTestCase.L21_START, + BasicPathTestCase.L21_DURATION, + STOP_D + ) + .bus( + BasicPathTestCase.LINE_31, + BasicPathTestCase.L31_START, + BasicPathTestCase.L31_DURATION, + STOP_E + ) + .egress(BasicPathTestCase.EGRESS_DURATION); + + assertEquals(BasicPathTestCase.BASIC_PATH_AS_STRING, path.toString(this::stopIndexToName)); + assertEquals( + BasicPathTestCase.BASIC_PATH_AS_DETAILED_STRING, + path.toStringDetailed(this::stopIndexToName) + ); + assertEquals(BasicPathTestCase.TOTAL_C1, path.c1()); + assertTrue(path.isC2Set()); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/AbstractStopArrival.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/AbstractStopArrival.java new file mode 100644 index 00000000000..bb4edf3a6a7 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/AbstractStopArrival.java @@ -0,0 +1,85 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +abstract class AbstractStopArrival implements ArrivalView { + + private final int round; + private final int stop; + private final int arrivalTime; + private final int c1; + private final int c2; + private final ArrivalView previous; + + AbstractStopArrival( + int round, + int stop, + int arrivalTime, + int extraCost, + int c2, + ArrivalView previous + ) { + this.round = round; + this.stop = stop; + this.arrivalTime = arrivalTime; + this.previous = previous; + this.c2 = c2; + + if (previous == null) { + this.c1 = extraCost; + } else { + this.c1 = previous.c1() + extraCost; + } + } + + AbstractStopArrival( + int round, + int stop, + int arrivalTime, + int extraCost, + ArrivalView previous + ) { + this(round, stop, arrivalTime, extraCost, previous.c2(), previous); + } + + @Override + public int stop() { + return stop; + } + + @Override + public int round() { + return round; + } + + @Override + public int arrivalTime() { + return arrivalTime; + } + + @Override + public int c1() { + return c1; + } + + @Override + public int c2() { + return c2; + } + + @Override + public ArrivalView previous() { + return previous; + } + + @Override + public String toString() { + return asString(); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Access.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Access.java new file mode 100644 index 00000000000..bb2fa1f481b --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Access.java @@ -0,0 +1,37 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import static org.opentripplanner.raptor.api.model.PathLegType.ACCESS; + +import org.opentripplanner.raptor.api.model.PathLegType; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.view.AccessPathView; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +class Access extends AbstractStopArrival { + + private final RaptorAccessEgress access; + + Access(int stop, int arrivalTime, RaptorAccessEgress path, int c2) { + super(0, stop, arrivalTime, path.c1(), c2, null); + this.access = path; + } + + @Override + public PathLegType arrivedBy() { + return ACCESS; + } + + @Override + public AccessPathView accessPath() { + return () -> access; + } + + @Override + public boolean arrivedOnBoard() { + return access.stopReachedOnBoard(); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/BasicPathTestCase.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/BasicPathTestCase.java new file mode 100644 index 00000000000..0130625784b --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/BasicPathTestCase.java @@ -0,0 +1,462 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.model.transfer.TransferConstraint.REGULAR_TRANSFER; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.utils.time.DurationUtils.durationToStr; +import static org.opentripplanner.utils.time.TimeUtils.time; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorConstrainedTransfer; +import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.raptor.api.path.AccessPathLeg; +import org.opentripplanner.raptor.api.path.EgressPathLeg; +import org.opentripplanner.raptor.api.path.PathLeg; +import org.opentripplanner.raptor.api.path.RaptorPath; +import org.opentripplanner.raptor.api.path.TransferPathLeg; +import org.opentripplanner.raptor.api.path.TransitPathLeg; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptor.path.Path; +import org.opentripplanner.raptor.rangeraptor.internalapi.WorkerLifeCycle; +import org.opentripplanner.raptor.rangeraptor.lifecycle.LifeCycleSubscriptions; +import org.opentripplanner.raptor.rangeraptor.path.DestinationArrival; +import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; + +/** + * This class is used to create a journeys with stop arrivals. + *

+ * It creates different data structures representing the same 'basic' trip to be used in + * unit-tests: + *

+ *   ~
+ *   Origin 10:00:15
+ *   ~ Walk 3m ~ A
+ *   ~ BUS L11 10:04 10:35 ~ B
+ *   ~ Walk 3m45s ~ C
+ *   ~ BUS L21 11:00 11:23 ~ D
+ *   ~ BUS L31 11:40 11:52 ~ E
+ *   ~ Walk 7m45s
+ *   ~ Destination 12:00
+ *
+ *   Duration: 1h59m45s
+ *   Transfers: 2
+ *   Generalized-cost: $8154
+ * 
+ * The Trip has 2 transfers, 1 connected by walking and without. The trip start at 10:00 and ends at + * 12:00, total 2 hours. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class BasicPathTestCase implements RaptorTestConstants { + + private static final RaptorConstrainedTransfer EMPTY_CONSTRAINTS = null; + + public static final String BASIC_PATH_AS_DETAILED_STRING = + "Walk 3m 10:00:15 10:03:15 C₁360 " + + "~ A 45s ~ " + + "BUS L11 10:04 10:35 31m C₁1_998 " + + "~ B 15s ~ " + + "Walk 3m45s 10:35:15 10:39 C₁450 " + + "~ C 21m ~ " + + "BUS L21 11:00 11:23 23m C₁2_640 " + + "~ D 17m ~ " + + "BUS L31 11:40 11:52 12m C₁1_776 " + + "~ E 15s ~ " + + "Walk 7m45s 11:52:15 12:00 C₁930 " + + "[10:00:15 12:00 1h59m45s Tₓ2 C₁8_154 C₂7]"; + + public static final String BASIC_PATH_AS_STRING = + "Walk 3m ~ A" + + " ~ BUS L11 10:04 10:35 ~ B" + + " ~ Walk 3m45s ~ C" + + " ~ BUS L21 11:00 11:23 ~ D" + + " ~ BUS L31 11:40 11:52 ~ E" + + " ~ Walk 7m45s " + + "[10:00:15 12:00 1h59m45s Tₓ2 C₁8_154 C₂7]"; + + private static final int BOARD_C1_SEC = 60; + private static final int TRANSFER_C1_SEC = 120; + private static final double[] TRANSIT_RELUCTANCE = new double[] { 1.0 }; + public static final int TRANSIT_RELUCTANCE_INDEX = 0; + public static final double WAIT_RELUCTANCE = 0.8; + private static final int C2 = 7; + + /** Stop cost for stop NA, A, C, E .. H is zero(0), B: 30s, and D: 60s. ?=0, A=1 .. H=8 */ + private static final int[] STOP_C1S = { 0, 0, 3_000, 0, 6_000, 0, 0, 0, 0, 0 }; + + // Some times which should not have eny effect on tests + private static final int VERY_EARLY = time("00:00"); + private static final int VERY_LATE = time("23:59"); + + public static final int RAPTOR_ITERATION_START_TIME = time("09:00"); + + // Access (Walk 3m15s ~ A) + public static final int ACCESS_START = time("10:00:15"); + public static final int ACCESS_END = time("10:03:15"); + public static final int ACCESS_DURATION = ACCESS_END - ACCESS_START; + public static final RaptorAccessEgress ACCESS_TRANSFER = TestAccessEgress.walk( + STOP_A, + ACCESS_DURATION + ); + public static final int ACCESS_C1 = ACCESS_TRANSFER.c1(); + public static final int ACCESS_C2 = 0; + + // Trip 1 (A ~ BUS L11 10:04 10:35 ~ B) + public static final int L11_START = time("10:04"); + private static final int L11_END = time("10:35"); + public static final int L11_DURATION = L11_END - L11_START; + private static final int L11_WAIT_DURATION = L11_START - ACCESS_END + ALIGHT_SLACK; + public static final int LINE_11_C1 = + STOP_C1S[STOP_A] + + STOP_C1S[STOP_B] + + toRaptorCost(BOARD_C1_SEC + WAIT_RELUCTANCE * L11_WAIT_DURATION + L11_DURATION); + public static final int LINE_11_C2 = 2; + + // Transfers (B ~ Walk 3m45s ~ C) + private static final int TX_START = time("10:35:15"); + private static final int TX_END = time("10:39:00"); + public static final int TX_DURATION = TX_END - TX_START; + public static final RaptorTransfer TX_TRANSFER = TestTransfer.transfer(STOP_C, TX_DURATION); + public static final int TX_C1 = TX_TRANSFER.c1(); + public static final int TX_C3 = 3; + + // Trip 2 (C ~ BUS L21 11:00 11:23 ~ D) + public static final int L21_START = time("11:00"); + private static final int L21_END = time("11:23"); + public static final int L21_DURATION = L21_END - L21_START; + private static final int L21_WAIT_DURATION = L21_START - TX_END + ALIGHT_SLACK; + public static final int LINE_21_C1 = + STOP_C1S[STOP_C] + + STOP_C1S[STOP_D] + + toRaptorCost( + BOARD_C1_SEC + TRANSFER_C1_SEC + WAIT_RELUCTANCE * L21_WAIT_DURATION + L21_DURATION + ); + public static final int LINE_21_C2 = 5; + + // Trip 3 (D ~ BUS L31 11:40 11:52 ~ E) + public static final int L31_START = time("11:40"); + private static final int L31_END = time("11:52"); + public static final int L31_DURATION = L31_END - L31_START; + private static final int L31_WAIT_DURATION = L31_START - (L21_END + ALIGHT_SLACK) + ALIGHT_SLACK; + public static final int LINE_31_C1 = + STOP_C1S[STOP_D] + + STOP_C1S[STOP_E] + + toRaptorCost( + BOARD_C1_SEC + TRANSFER_C1_SEC + WAIT_RELUCTANCE * L31_WAIT_DURATION + L31_DURATION + ); + public static final int LINE_31_C2 = 6; + + // Egress (E ~ Walk 7m45s ~ ) + public static final int EGRESS_START = time("11:52:15"); + public static final int EGRESS_END = time("12:00"); + public static final int EGRESS_DURATION = EGRESS_END - EGRESS_START; + public static final RaptorAccessEgress EGRESS_TRANSFER = TestAccessEgress.walk( + STOP_E, + EGRESS_DURATION + ); + public static final int EGRESS_C1 = EGRESS_TRANSFER.c1(); + public static final int EGRESS_C2 = 7; + + public static final int TRIP_DURATION = EGRESS_END - ACCESS_START; + + private static final RaptorAccessEgress ACCESS = TestAccessEgress.walk( + STOP_A, + ACCESS_DURATION, + ACCESS_C1 + ); + private static final RaptorAccessEgress EGRESS = TestAccessEgress.walk( + STOP_E, + EGRESS_DURATION, + EGRESS_C1 + ); + // this is of course not a real flex egress + private static final RaptorAccessEgress FLEX = TestAccessEgress.flexWithOnBoard( + STOP_E, + EGRESS_DURATION, + EGRESS_C1 + ); + + public static final String LINE_11 = "L11"; + public static final String LINE_21 = "L21"; + public static final String LINE_31 = "L31"; + + public static final TestTripSchedule TRIP_1 = TestTripSchedule + .schedule(TestTripPattern.pattern(LINE_11, STOP_A, STOP_B)) + .times(L11_START, L11_END) + .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) + .build(); + + public static final TestTripSchedule TRIP_2 = TestTripSchedule + .schedule(TestTripPattern.pattern(LINE_21, STOP_C, STOP_D)) + .times(L21_START, L21_END) + .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) + .build(); + + public static final TestTripSchedule TRIP_3 = TestTripSchedule + .schedule(TestTripPattern.pattern(LINE_31, STOP_D, STOP_E)) + // The early arrival and late departure should not have any effect on tests + .arrivals(VERY_EARLY, L31_END) + .departures(L31_START, VERY_LATE) + .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) + .build(); + + public static final RaptorCostCalculator C1_CALCULATOR = new DefaultCostCalculator<>( + BOARD_C1_SEC, + TRANSFER_C1_SEC, + WAIT_RELUCTANCE, + TRANSIT_RELUCTANCE, + STOP_C1S + ); + + public static final int TOTAL_C1 = + ACCESS_C1 + LINE_11_C1 + TX_C1 + LINE_21_C1 + LINE_31_C1 + EGRESS_C1; + + /** Wait time between trip L11 and L21 including slack */ + public static final int WAIT_TIME_L11_L21 = L21_START - L11_END - TX_DURATION; + + /** Wait time between trip L21 and L31 including slack */ + public static final int WAIT_TIME_L21_L31 = L31_START - L21_END; + + public static WorkerLifeCycle lifeCycle() { + return new LifeCycleSubscriptions(); + } + + public static DestinationArrival basicTripByForwardSearch() { + ArrivalView prevArrival, egress; + prevArrival = TestArrivals.access(STOP_A, ACCESS_START, ACCESS_END, ACCESS_C1, ACCESS_C2); + prevArrival = TestArrivals.bus(1, STOP_B, L11_END, LINE_11_C1, LINE_11_C2, TRIP_1, prevArrival); + prevArrival = TestArrivals.transfer(1, STOP_C, TX_START, TX_END, TX_C1, prevArrival); + prevArrival = TestArrivals.bus(2, STOP_D, L21_END, LINE_21_C1, LINE_21_C2, TRIP_2, prevArrival); + prevArrival = TestArrivals.bus(3, STOP_E, L31_END, LINE_31_C1, LINE_31_C2, TRIP_3, prevArrival); + egress = TestArrivals.egress(EGRESS_START, EGRESS_END, EGRESS_C1, EGRESS_C2, prevArrival); + return new DestinationArrival<>( + egress.egressPath().egress(), + egress.previous(), + egress.arrivalTime(), + egress.egressPath().egress().c1(), + egress.c2() + ); + } + + /** + * This is the same itinerary as {@link #basicTripByForwardSearch()}, as found by a reverse + * search: + */ + public static DestinationArrival basicTripByReverseSearch() { + ArrivalView nextArrival, egress; + nextArrival = TestArrivals.access(STOP_E, EGRESS_END, EGRESS_START, EGRESS_C1, EGRESS_C2); + // Board slack is subtracted from the arrival time to get the latest possible + nextArrival = + TestArrivals.bus(1, STOP_D, L31_START, LINE_31_C1, LINE_31_C2, TRIP_3, nextArrival); + nextArrival = + TestArrivals.bus(2, STOP_C, L21_START, LINE_21_C1, LINE_21_C2, TRIP_2, nextArrival); + nextArrival = TestArrivals.transfer(2, STOP_B, TX_END, TX_START, TX_C1, nextArrival); + nextArrival = + TestArrivals.bus(3, STOP_A, L11_START, LINE_11_C1, LINE_11_C2, TRIP_1, nextArrival); + egress = TestArrivals.egress(ACCESS_END, ACCESS_START, ACCESS_C1, ACCESS_C2, nextArrival); + return new DestinationArrival<>( + egress.egressPath().egress(), + egress.previous(), + egress.arrivalTime(), + egress.egressPath().egress().c1(), + egress.c2() + ); + } + + /** + * Both {@link #basicTripByForwardSearch()} and {@link #basicTripByReverseSearch()} should return + * the same trip, here returned as a path. + */ + public static RaptorPath basicTripAsPath() { + PathLeg leg6 = new EgressPathLeg<>( + EGRESS, + EGRESS_START, + EGRESS_END, + EGRESS_C1 + ); + TransitPathLeg leg5 = new TransitPathLeg<>( + TRIP_3, + L31_START, + L31_END, + TRIP_3.findDepartureStopPosition(L31_START, STOP_D), + TRIP_3.findArrivalStopPosition(L31_END, STOP_E), + EMPTY_CONSTRAINTS, + LINE_31_C1, + leg6 + ); + TransitPathLeg leg4 = new TransitPathLeg<>( + TRIP_2, + L21_START, + L21_END, + TRIP_2.findDepartureStopPosition(L21_START, STOP_C), + TRIP_2.findArrivalStopPosition(L21_END, STOP_D), + EMPTY_CONSTRAINTS, + LINE_21_C1, + leg5 + ); + var transfer = TestTransfer.transfer(STOP_C, TX_END - TX_START); + PathLeg leg3 = new TransferPathLeg<>( + STOP_B, + TX_START, + TX_END, + transfer.c1(), + transfer, + leg4.asTransitLeg() + ); + var leg2 = new TransitPathLeg<>( + TRIP_1, + L11_START, + L11_END, + TRIP_1.findDepartureStopPosition(L11_START, STOP_A), + TRIP_1.findArrivalStopPosition(L11_END, STOP_B), + EMPTY_CONSTRAINTS, + LINE_11_C1, + leg3 + ); + AccessPathLeg leg1 = new AccessPathLeg<>( + ACCESS, + ACCESS_START, + ACCESS_END, + ACCESS_C1, + leg2.asTransitLeg() + ); + return new Path<>(RAPTOR_ITERATION_START_TIME, leg1, TOTAL_C1, 7); + } + + public static RaptorPath flexTripAsPath() { + PathLeg leg6 = new EgressPathLeg<>(FLEX, EGRESS_START, EGRESS_END, EGRESS_C1); + var transfer = TestTransfer.transfer(STOP_E, TX_END - TX_START); + PathLeg leg3 = new TransferPathLeg<>( + STOP_B, + TX_START, + TX_END, + transfer.c1(), + transfer, + leg6 + ); + var leg2 = new TransitPathLeg<>( + TRIP_1, + L11_START, + L11_END, + TRIP_1.findDepartureStopPosition(L11_START, STOP_A), + TRIP_1.findArrivalStopPosition(L11_END, STOP_B), + EMPTY_CONSTRAINTS, + LINE_11_C1, + leg3 + ); + AccessPathLeg leg1 = new AccessPathLeg<>( + ACCESS, + ACCESS_START, + ACCESS_END, + ACCESS_C1, + leg2.asTransitLeg() + ); + return new Path<>(RAPTOR_ITERATION_START_TIME, leg1, TOTAL_C1, C2); + } + + public static List basicTripStops() { + return Arrays.asList(STOP_A, STOP_B, STOP_C, STOP_D, STOP_E); + } + + @Test + public void testSetup() { + // Assert test data is configured correct + assertEquals(ACCESS_END + BOARD_SLACK, L11_START); + assertEquals(BOARD_SLACK + ALIGHT_SLACK, L11_WAIT_DURATION); + assertEquals(L31_END + ALIGHT_SLACK, EGRESS_START); + assertEquals( + durationToStr(TRIP_DURATION), + durationToStr( + ACCESS_DURATION + + L11_DURATION + + L11_WAIT_DURATION + + TX_DURATION + + L21_DURATION + + L21_WAIT_DURATION + + L31_DURATION + + L31_WAIT_DURATION + + EGRESS_DURATION + ), + "Access: " + + durationToStr(ACCESS_DURATION) + + ", Line 11: " + + durationToStr(L11_DURATION) + + " (wait " + + durationToStr(L11_WAIT_DURATION) + + ")" + + ", Tx: " + + durationToStr(TX_DURATION) + + ", Line 21: " + + durationToStr(L21_DURATION) + + " (wait " + + durationToStr(L21_WAIT_DURATION) + + ")" + + ", Line 31: " + + durationToStr(L31_DURATION) + + " (wait " + + durationToStr(L31_WAIT_DURATION) + + ")" + + ", Egress: " + + durationToStr(EGRESS_DURATION) + ); + + // The calculator is not under test here, so we assert everything is as expected + assertEquals( + LINE_11_C1, + transitArrivalCost(ACCESS_END, TRIP_1, STOP_A, L11_START, STOP_B, L11_END) + ); + assertEquals( + LINE_21_C1, + transitArrivalCost(TX_END, TRIP_2, STOP_C, L21_START, STOP_D, L21_END) + ); + assertEquals( + LINE_31_C1, + transitArrivalCost(L21_END + ALIGHT_SLACK, TRIP_3, STOP_D, L31_START, STOP_E, L31_END) + ); + + assertEquals(BASIC_PATH_AS_STRING, basicTripAsPath().toString(this::stopIndexToName)); + + assertEquals( + BASIC_PATH_AS_DETAILED_STRING, + basicTripAsPath().toStringDetailed(this::stopIndexToName) + ); + } + + private static int transitArrivalCost( + int prevArrivalTime, + TestTripSchedule trip, + int boardStop, + int boardTime, + int alightStop, + int alightTime + ) { + boolean firstTransit = TRIP_1 == trip; + int boardCost = C1_CALCULATOR.boardingCost( + firstTransit, + prevArrivalTime, + boardStop, + boardTime, + trip, + REGULAR_TRANSFER + ); + + return C1_CALCULATOR.transitArrivalCost( + boardCost, + ALIGHT_SLACK, + alightTime - boardTime, + trip, + alightStop + ); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Egress.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Egress.java new file mode 100644 index 00000000000..9b781b0beb0 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Egress.java @@ -0,0 +1,54 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import org.opentripplanner.raptor.api.model.PathLegType; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptor.api.view.EgressPathView; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; +import org.opentripplanner.utils.time.TimeUtils; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class Egress extends AbstractStopArrival { + + private final RaptorAccessEgress egressPath; + + public Egress( + int arrivalTime, + RaptorAccessEgress egressPath, + int c2, + ArrivalView previous + ) { + super(previous.round(), previous.stop(), arrivalTime, egressPath.c1(), c2, previous); + this.egressPath = egressPath; + } + + @Override + public EgressPathView egressPath() { + return () -> egressPath; + } + + @Override + public String toString() { + return String.format( + "Egress { round: %d, stop: %d, arrival-time: %s $%d }", + round(), + stop(), + TimeUtils.timeToStrCompact(arrivalTime()), + c1() + ); + } + + @Override + public PathLegType arrivedBy() { + return PathLegType.EGRESS; + } + + @Override + public boolean arrivedOnBoard() { + return egressPath.stopReachedOnBoard(); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/FlexAccessAndEgressPathTestCase.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/FlexAccessAndEgressPathTestCase.java similarity index 90% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/FlexAccessAndEgressPathTestCase.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/FlexAccessAndEgressPathTestCase.java index 51639ecc72d..99dd81ebbb7 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/FlexAccessAndEgressPathTestCase.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/FlexAccessAndEgressPathTestCase.java @@ -1,28 +1,26 @@ -package org.opentripplanner.raptor._data.stoparrival; +package org.opentripplanner.raptorlegacy._data.stoparrival; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.access; -import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.bus; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; import static org.opentripplanner.raptor.api.model.RaptorValueFormatter.formatC1; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toRaptorCost; import static org.opentripplanner.utils.time.DurationUtils.durationInSeconds; import static org.opentripplanner.utils.time.TimeUtils.time; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestTransfer; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; import org.opentripplanner.raptor.api.view.ArrivalView; import org.opentripplanner.raptor.rangeraptor.path.DestinationArrival; import org.opentripplanner.raptor.spi.DefaultSlackProvider; import org.opentripplanner.raptor.spi.RaptorSlackProvider; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.utils.time.TimeUtils; /** @@ -44,7 +42,11 @@ *
  • Walk transfer
  • *
  • Flex
  • * + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. */ +@Deprecated public class FlexAccessAndEgressPathTestCase implements RaptorTestConstants { private static final int ZERO = 0; @@ -292,19 +294,19 @@ private static DestinationArrival flexForwardSearch( if (LINE_A.equals(line)) { // The latest time the access can arrive is the same as the TX1 arrival time in case B arrivalTime = accessPath.latestArrivalTime(TX1_END); - prevArrival = access(accessPath.stop(), arrivalTime, accessPath); + prevArrival = TestArrivals.access(accessPath.stop(), arrivalTime, accessPath); int waitCost = costL1ForwardIncWait(prevArrival.arrivalTime()); - prevArrival = bus(2, STOP_D, L1_STOP_ARR_TIME, waitCost, 0, TRIP_A, prevArrival); + prevArrival = TestArrivals.bus(2, STOP_D, L1_STOP_ARR_TIME, waitCost, 0, TRIP_A, prevArrival); } else { arrivalTime = accessPath.latestArrivalTime(TX1_START); - prevArrival = access(accessPath.stop(), arrivalTime, accessPath); + prevArrival = TestArrivals.access(accessPath.stop(), arrivalTime, accessPath); int timeShift = TX1_START - prevArrival.arrivalTime(); prevArrival = new Transfer(1, TX1_END - timeShift, TX1_TRANSFER, prevArrival); int waitCost = costL1ForwardIncWait(prevArrival.arrivalTime()); - prevArrival = bus(2, STOP_C, L1_STOP_ARR_TIME, waitCost, 0, TRIP_B, prevArrival); + prevArrival = TestArrivals.bus(2, STOP_C, L1_STOP_ARR_TIME, waitCost, 0, TRIP_B, prevArrival); prevArrival = new Transfer(2, TX2_END, TX2_TRANSFER, prevArrival); } @@ -338,18 +340,18 @@ private static DestinationArrival flexReverseSearch( if (LINE_A.equals(line)) { arrivalTime = L1_END + ALIGHT_SLACK + TRANSFER_SLACK; arrivalTime = egressPath.earliestDepartureTime(arrivalTime); - prevArrival = access(egressPath.stop(), arrivalTime, egressPath); + prevArrival = TestArrivals.access(egressPath.stop(), arrivalTime, egressPath); cost = costL1ReverseIncWait(prevArrival.arrivalTime()); - prevArrival = bus(2, STOP_A, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_A, prevArrival); + prevArrival = TestArrivals.bus(2, STOP_A, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_A, prevArrival); } else { arrivalTime = L1_END + ALIGHT_SLACK + TX2_DURATION + TRANSFER_SLACK; arrivalTime = egressPath.earliestDepartureTime(arrivalTime); - prevArrival = access(egressPath.stop(), arrivalTime, egressPath); + prevArrival = TestArrivals.access(egressPath.stop(), arrivalTime, egressPath); arrivalTime = prevArrival.arrivalTime() - TX2_DURATION; prevArrival = new Transfer(1, arrivalTime, TX2_TRANSFER_REV, prevArrival); cost = costL1ReverseIncWait(prevArrival.arrivalTime()); - prevArrival = bus(2, STOP_B, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_B, prevArrival); + prevArrival = TestArrivals.bus(2, STOP_B, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_B, prevArrival); arrivalTime = prevArrival.arrivalTime() - TX1_DURATION; prevArrival = new Transfer(2, arrivalTime, TX1_TRANSFER_REV, prevArrival); } diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/TestArrivals.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/TestArrivals.java new file mode 100644 index 00000000000..4ea0398efa4 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/TestArrivals.java @@ -0,0 +1,110 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class TestArrivals { + + public static ArrivalView access( + int stop, + int arrivalTime, + RaptorAccessEgress path, + int c2 + ) { + return new Access(stop, arrivalTime, path, c2); + } + + public static ArrivalView access( + int stop, + int arrivalTime, + RaptorAccessEgress path + ) { + return access(stop, arrivalTime, path, RaptorConstants.NOT_SET); + } + + public static ArrivalView access( + int stop, + int departureTime, + int arrivalTime, + int c1, + int c2 + ) { + return access( + stop, + arrivalTime, + TestAccessEgress.walk(stop, Math.abs(arrivalTime - departureTime), c1), + c2 + ); + } + + public static ArrivalView access( + int stop, + int departureTime, + int arrivalTime, + int c1 + ) { + return access(stop, departureTime, arrivalTime, c1, RaptorConstants.NOT_SET); + } + + public static ArrivalView transfer( + int round, + int arrivalTime, + RaptorTransfer transfer, + ArrivalView previous + ) { + return new Transfer(round, arrivalTime, transfer, previous); + } + + public static ArrivalView transfer( + int round, + int stop, + int departureTime, + int arrivalTime, + int extraCost, + ArrivalView previous + ) { + return transfer( + round, + arrivalTime, + TestTransfer.transfer(stop, Math.abs(arrivalTime - departureTime), extraCost), + previous + ); + } + + public static ArrivalView bus( + int round, + int stop, + int arrivalTime, + int c1, + int c2, + TestTripSchedule trip, + ArrivalView previous + ) { + return new Transit(round, stop, arrivalTime, c1, c2, trip, previous); + } + + public static ArrivalView egress( + int departureTime, + int arrivalTime, + int c1, + int c2, + ArrivalView previous + ) { + return new Egress( + departureTime, + TestAccessEgress.walk(previous.stop(), Math.abs(arrivalTime - departureTime), c1), + c2, + previous + ); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transfer.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transfer.java new file mode 100644 index 00000000000..56363c4fdde --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transfer.java @@ -0,0 +1,43 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import static org.opentripplanner.raptor.api.model.PathLegType.TRANSFER; + +import org.opentripplanner.raptor.api.model.PathLegType; +import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; + +/* + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +class Transfer extends AbstractStopArrival { + + private final RaptorTransfer transfer; + + Transfer( + int round, + int arrivalTime, + RaptorTransfer transfer, + ArrivalView previous + ) { + super(round, transfer.stop(), arrivalTime, transfer.c1(), previous.c2(), previous); + this.transfer = transfer; + } + + @Override + public PathLegType arrivedBy() { + return TRANSFER; + } + + @Override + public RaptorTransfer transfer() { + return transfer; + } + + @Override + public boolean arrivedOnBoard() { + return false; + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transit.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transit.java new file mode 100644 index 00000000000..5de1cdbd4dc --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/stoparrival/Transit.java @@ -0,0 +1,57 @@ +package org.opentripplanner.raptorlegacy._data.stoparrival; + +import static org.opentripplanner.raptor.api.model.PathLegType.TRANSIT; + +import org.opentripplanner.raptor.api.model.PathLegType; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptor.api.view.TransitPathView; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; + +/** + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +class Transit extends AbstractStopArrival implements TransitPathView { + + private final TestTripSchedule trip; + + Transit( + int round, + int stop, + int arrivalTime, + int c1, + int c2, + TestTripSchedule trip, + ArrivalView previous + ) { + super(round, stop, arrivalTime, c1, c2, previous); + this.trip = trip; + } + + @Override + public PathLegType arrivedBy() { + return TRANSIT; + } + + @Override + public TransitPathView transitPath() { + return this; + } + + @Override + public int boardStop() { + return previous().stop(); + } + + @Override + public TestTripSchedule trip() { + return trip; + } + + @Override + public boolean arrivedOnBoard() { + return true; + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestAccessEgress.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestAccessEgress.java new file mode 100644 index 00000000000..6fb26c22dfb --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestAccessEgress.java @@ -0,0 +1,365 @@ +package org.opentripplanner.raptorlegacy._data.transit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptorlegacy._data.RaptorTestConstants.SECONDS_IN_A_DAY; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.utils.time.TimeUtils; + +/** + * Simple implementation for {@link RaptorAccessEgress} for use in unit-tests. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class TestAccessEgress implements RaptorAccessEgress { + + public static final int DEFAULT_NUMBER_OF_RIDES = 0; + public static final boolean STOP_REACHED_ON_BOARD = true; + public static final boolean STOP_REACHED_ON_FOOT = false; + public static final double DEFAULT_WALK_RELUCTANCE = 2.0; + + private final int stop; + private final int durationInSeconds; + private final int c1; + private final int numberOfRides; + private final boolean stopReachedOnBoard; + private final boolean free; + private final Integer opening; + private final Integer closing; + private final boolean closed; + private final int timePenalty; + + private TestAccessEgress(Builder builder) { + this.stop = builder.stop; + this.durationInSeconds = builder.durationInSeconds; + this.numberOfRides = builder.numberOfRides; + this.stopReachedOnBoard = builder.stopReachedOnBoard; + this.free = builder.free; + this.opening = builder.opening; + this.closing = builder.closing; + this.closed = builder.closed; + this.timePenalty = builder.timePenalty; + this.c1 = builder.c1; + + if (free) { + assertEquals(0, durationInSeconds); + } else { + assertTrue(durationInSeconds > 0); + } + if (closed) { + assertNull(opening); + assertNull(closing); + } + assertTrue(numberOfRides >= 0); + } + + public static TestAccessEgress free(int stop) { + return new Builder(stop, 0).withFree().build(); + } + + /** + * @deprecated A stop cannot be both free and have a cost - This is not a valid + * access/egress. + */ + @Deprecated + public static TestAccessEgress free(int stop, int cost) { + return new Builder(stop, 0).withFree().withCost(cost).build(); + } + + public static TestAccessEgress walk(int stop, int durationInSeconds) { + return new Builder(stop, durationInSeconds).build(); + } + + public static TestAccessEgress walk(int stop, int durationInSeconds, double walkReluctance) { + return walk(stop, durationInSeconds, walkCost(durationInSeconds, walkReluctance)); + } + + public static TestAccessEgress walk(int stop, int durationInSeconds, int cost) { + return new Builder(stop, durationInSeconds).withCost(cost).build(); + } + + public static TestAccessEgress flexWithOnBoard(int stop, int durationInSeconds, int cost) { + return new Builder(stop, durationInSeconds) + .withCost(cost) + .withNRides(1) + .stopReachedOnBoard() + .build(); + } + + /** Create a new flex access and arrive stop onBoard with 1 ride/extra transfer. */ + public static TestAccessEgress flex(int stop, int durationInSeconds) { + return flex(stop, durationInSeconds, 1, walkCost(durationInSeconds)); + } + + /** Create a new flex access and arrive stop onBoard with 1 ride/extra transfer. */ + public static TestAccessEgress flex(int stop, int durationInSeconds, int nRides) { + return flex(stop, durationInSeconds, nRides, walkCost(durationInSeconds)); + } + + /** Create a new flex access and arrive stop onBoard. */ + public static TestAccessEgress flex(int stop, int durationInSeconds, int nRides, int cost) { + assert nRides > DEFAULT_NUMBER_OF_RIDES; + return new Builder(stop, durationInSeconds) + .stopReachedOnBoard() + .withNRides(nRides) + .withCost(cost) + .build(); + } + + /** Create a flex access arriving at given stop by walking with 1 ride/extra transfer. */ + public static TestAccessEgress flexAndWalk(int stop, int durationInSeconds) { + return flexAndWalk(stop, durationInSeconds, 1, walkCost(durationInSeconds)); + } + + /** Create a flex access arriving at given stop by walking with 1 ride/extra transfer. */ + public static TestAccessEgress flexAndWalk(int stop, int durationInSeconds, int nRides) { + return flexAndWalk(stop, durationInSeconds, nRides, walkCost(durationInSeconds)); + } + + /** Create a flex access arriving at given stop by walking. */ + public static TestAccessEgress flexAndWalk( + int stop, + int durationInSeconds, + int nRides, + int cost + ) { + assert nRides > DEFAULT_NUMBER_OF_RIDES; + return new Builder(stop, durationInSeconds).withNRides(nRides).withCost(cost).build(); + } + + public static Collection transfers(int... stopTimes) { + List legs = new ArrayList<>(); + for (int i = 0; i < stopTimes.length; i += 2) { + legs.add(walk(stopTimes[i], stopTimes[i + 1])); + } + return legs; + } + + public static int walkCost(int durationInSeconds) { + return walkCost(durationInSeconds, DEFAULT_WALK_RELUCTANCE); + } + + public static int walkCost(int durationInSeconds, double reluctance) { + return toRaptorCost(durationInSeconds * reluctance); + } + + /** + * Add opening and closing hours and return a new object. + *

    + * Opening and closing is specified as seconds since the start of "RAPTOR time" to limit the + * time periods that the access is traversable, which is repeatead every 24 hours. This allows + * access to only be traversable between given times like 08:00 and 16:00 every day. + */ + public TestAccessEgress openingHours(int opening, int closing) { + return copyOf().withOpeningHours(opening, closing).build(); + } + + /** Alias for {@code openingHours(TimeUtils.time(opening), TimeUtils.time(closing))} */ + public TestAccessEgress openingHours(String opening, String closing) { + return openingHours(TimeUtils.time(opening), TimeUtils.time(closing)); + } + + public TestAccessEgress openingHoursClosed() { + return copyOf().withClosed().build(); + } + + public TestAccessEgress withTimePenalty(int timePenalty) { + return this.copyOf().withTimePenalty(timePenalty).build(); + } + + public Builder copyOf() { + return new Builder(this); + } + + @Override + public int stop() { + return stop; + } + + @Override + public int c1() { + return c1; + } + + @Override + public int durationInSeconds() { + return durationInSeconds; + } + + @Override + public int timePenalty() { + return timePenalty; + } + + @Override + public int earliestDepartureTime(int requestedDepartureTime) { + if (!hasOpeningHours()) { + return requestedDepartureTime; + } + if (closed) { + return RaptorConstants.TIME_NOT_SET; + } + + int days = Math.floorDiv(requestedDepartureTime, SECONDS_IN_A_DAY); + int specificOpening = days * SECONDS_IN_A_DAY + opening; + int specificClosing = days * SECONDS_IN_A_DAY + closing; + + if (requestedDepartureTime < specificOpening) { + return specificOpening; + } else if (requestedDepartureTime > specificClosing) { + // return the opening time for the next day + return specificOpening + SECONDS_IN_A_DAY; + } + return requestedDepartureTime; + } + + @Override + public int latestArrivalTime(int requestedArrivalTime) { + if (!hasOpeningHours()) { + return requestedArrivalTime; + } + if (closed) { + return RaptorConstants.TIME_NOT_SET; + } + + // opening & closing is relative to the departure + int requestedDepartureTime = requestedArrivalTime - durationInSeconds(); + int days = Math.floorDiv(requestedDepartureTime, SECONDS_IN_A_DAY); + int specificOpening = days * SECONDS_IN_A_DAY + opening; + int specificClosing = days * SECONDS_IN_A_DAY + closing; + int closeAtArrival = specificClosing + durationInSeconds(); + + if (requestedDepartureTime < specificOpening) { + // return the closing for the previous day, offset with durationInSeconds() + return closeAtArrival - SECONDS_IN_A_DAY; + } else if (requestedArrivalTime > closeAtArrival) { + return closeAtArrival; + } + return requestedArrivalTime; + } + + @Override + public boolean hasOpeningHours() { + return closed || opening != null || closing != null; + } + + @Override + public int numberOfRides() { + return numberOfRides; + } + + @Override + public boolean stopReachedOnBoard() { + return stopReachedOnBoard; + } + + @Override + public boolean isFree() { + return this.free; + } + + @Override + public String toString() { + return asString(true, true, null); + } + + /** + * Do not use the builder, use the static factory methods. Only use the builder if you need to + * override the {@link TestAccessEgress class}. + */ + protected static class Builder { + + int stop; + int durationInSeconds; + int c1; + int numberOfRides = DEFAULT_NUMBER_OF_RIDES; + boolean stopReachedOnBoard = STOP_REACHED_ON_FOOT; + Integer opening = null; + Integer closing = null; + private boolean free = false; + private boolean closed = false; + private int timePenalty; + + Builder(int stop, int durationInSeconds) { + this.stop = stop; + this.durationInSeconds = durationInSeconds; + this.c1 = walkCost(durationInSeconds); + this.timePenalty = RaptorConstants.TIME_NOT_SET; + } + + Builder(TestAccessEgress original) { + this.free = original.free; + this.stop = original.stop; + this.durationInSeconds = original.durationInSeconds; + this.stopReachedOnBoard = original.stopReachedOnBoard; + this.c1 = original.c1; + this.numberOfRides = original.numberOfRides; + this.opening = original.opening; + this.closing = original.closing; + this.closed = original.closed; + this.timePenalty = original.timePenalty; + } + + Builder withFree() { + this.free = true; + this.durationInSeconds = 0; + return this; + } + + Builder withCost(int cost) { + this.c1 = cost; + return this; + } + + Builder withNRides(int numberOfRides) { + this.numberOfRides = numberOfRides; + return this; + } + + Builder stopReachedOnBoard() { + this.stopReachedOnBoard = STOP_REACHED_ON_BOARD; + return this; + } + + Builder withTimePenalty(int timePenalty) { + this.timePenalty = timePenalty; + return this; + } + + Builder withOpeningHours(int opening, int closing) { + if (opening > closing) { + throw new IllegalStateException( + "Must open before is close. Opens at " + + TimeUtils.timeToStrCompact(opening) + + " and close at " + + TimeUtils.timeToStrCompact(closing) + + "." + ); + } + this.closed = false; + this.opening = opening; + this.closing = closing; + return this; + } + + Builder withClosed() { + this.opening = null; + this.closing = null; + this.closed = true; + return this; + } + + TestAccessEgress build() { + return new TestAccessEgress(this); + } + } +} diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedBoardingSearch.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedBoardingSearch.java new file mode 100644 index 00000000000..2766f760067 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedBoardingSearch.java @@ -0,0 +1,128 @@ +package org.opentripplanner.raptorlegacy._data.transit; + +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.List; +import java.util.function.BiPredicate; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.opentripplanner.model.transfer.TransferConstraint; +import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; +import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch; +import org.opentripplanner.raptor.spi.RaptorTimeTable; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public class TestConstrainedBoardingSearch + implements RaptorConstrainedBoardingSearch { + + /** Index of guaranteed transfers by fromStopPos */ + private final TIntObjectMap> transfersByFromStopPos = new TIntObjectHashMap<>(); + private final BitSet transfersByToStopPosExist = new BitSet(); + private final BiPredicate timeAfterOrEqual; + private int currentTargetStopPos; + + TestConstrainedBoardingSearch(boolean forward) { + this.timeAfterOrEqual = forward ? (a, b) -> a >= b : (a, b) -> a <= b; + } + + @Override + public boolean transferExistTargetStop(int targetStopPos) { + this.currentTargetStopPos = targetStopPos; + return transfersByFromStopPos.containsKey(targetStopPos); + } + + @Override + public boolean transferExistSourceStop(int targetStopPos) { + // This is only used to check for + return transfersByToStopPosExist.get(targetStopPos); + } + + @Nullable + @Override + public RaptorBoardOrAlightEvent find( + RaptorTimeTable targetTimetable, + int transferSlack, + TestTripSchedule sourceTrip, + int sourceStopIndex, + int prevTransitArrivalTime, + int earliestBoardTime + ) { + var list = transfersByFromStopPos.get(currentTargetStopPos); + for (TestConstrainedTransfer tx : list) { + var trip = tx.getSourceTrip(); + if (trip == sourceTrip) { + int stopPos = trip.findDepartureStopPosition(prevTransitArrivalTime, sourceStopIndex); + boolean boardAlightPossible = timeAfterOrEqual.test(tx.getTime(), prevTransitArrivalTime); + if (tx.getSourceStopPos() == stopPos && boardAlightPossible) { + return tx.boardingEvent(tx.isFacilitated() ? prevTransitArrivalTime : earliestBoardTime); + } + } + } + return RaptorBoardOrAlightEvent.empty(earliestBoardTime); + } + + /** + * Return boardings as a result for constrained transfers like a guaranteed transfer. + */ + public List constrainedBoardings() { + return transfersByFromStopPos + .valueCollection() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + @Override + public String toString() { + return ToStringBuilder + .of(TestConstrainedBoardingSearch.class) + .addNum("currentTargetStopPos", currentTargetStopPos) + .addObj("index", transfersByFromStopPos) + .toString(); + } + + /** + * The the {@code source/target} is the trips in order of the search direction (forward or + * reverse). For reverse search it is the opposite from {@code from/to} in the result path. + */ + void addConstraintTransfers( + TestTripSchedule sourceTrip, + int sourceStopPos, + TestTripSchedule targetTrip, + int targetTripIndex, + int targetStopPos, + int targetTime, + TransferConstraint constraint + ) { + List list = transfersByFromStopPos.get(targetStopPos); + if (list == null) { + list = new ArrayList<>(); + transfersByFromStopPos.put(targetStopPos, list); + } + list.add( + new TestConstrainedTransfer( + constraint, + sourceTrip, + sourceStopPos, + targetTrip, + targetTripIndex, + targetStopPos, + targetTime + ) + ); + transfersByToStopPosExist.set(sourceStopPos); + } + + void clear() { + transfersByFromStopPos.clear(); + transfersByToStopPosExist.clear(); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedTransfer.java similarity index 91% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedTransfer.java index 643585f8c5b..034a029787d 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestConstrainedTransfer.java @@ -1,4 +1,4 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import javax.annotation.Nullable; import org.opentripplanner.model.transfer.TransferConstraint; @@ -8,6 +8,11 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.constrainedtransfer.ConstrainedTransferBoarding; import org.opentripplanner.utils.tostring.ToStringBuilder; +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated class TestConstrainedTransfer implements RaptorConstrainedTransfer { private final TransferConstraint transferConstraints; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestRoute.java similarity index 95% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestRoute.java index 6a606876e0e..715021521ef 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestRoute.java @@ -1,4 +1,4 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import java.util.ArrayList; import java.util.Collections; @@ -13,6 +13,11 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripScheduleSearchFactory; import org.opentripplanner.utils.tostring.ToStringBuilder; +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated public class TestRoute implements RaptorRoute, RaptorTimeTable { private final TestTripPattern pattern; diff --git a/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransfer.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransfer.java new file mode 100644 index 00000000000..dead63d0135 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransfer.java @@ -0,0 +1,52 @@ +package org.opentripplanner.raptorlegacy._data.transit; + +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; + +import org.opentripplanner.raptor.api.model.RaptorTransfer; + +/** + * Simple implementation for {@link RaptorTransfer} for use in unit-tests. + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated +public record TestTransfer(int stop, int durationInSeconds, int cost) implements RaptorTransfer { + public static final double DEFAULT_WALK_RELUCTANCE = 2.0; + + public static TestTransfer transfer(int stop, int durationInSeconds) { + return new TestTransfer(stop, durationInSeconds, walkCost(durationInSeconds)); + } + + public static TestTransfer transfer(int stop, int durationInSeconds, int cost) { + return new TestTransfer(stop, durationInSeconds, cost); + } + + public static int walkCost(int durationInSeconds) { + return walkCost(durationInSeconds, DEFAULT_WALK_RELUCTANCE); + } + + public static int walkCost(int durationInSeconds, double reluctance) { + return toRaptorCost(durationInSeconds * reluctance); + } + + @Override + public int stop() { + return stop; + } + + @Override + public int c1() { + return cost; + } + + @Override + public int durationInSeconds() { + return durationInSeconds; + } + + @Override + public String toString() { + return asString(); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferPoint.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransferPoint.java similarity index 84% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferPoint.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransferPoint.java index ef2271e51ae..8c274f779d8 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferPoint.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransferPoint.java @@ -1,8 +1,13 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import org.opentripplanner.model.transfer.TransferPoint; import org.opentripplanner.utils.tostring.ToStringBuilder; +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated public class TestTransferPoint implements TransferPoint { private final int stop; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransitData.java similarity index 95% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransitData.java index 4f225106ba2..1cbf591c01d 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTransitData.java @@ -1,8 +1,8 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; -import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptorlegacy._data.transit.TestRoute.route; +import static org.opentripplanner.raptorlegacy._data.transit.TestTripPattern.pattern; +import static org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule.schedule; import java.util.ArrayList; import java.util.BitSet; @@ -13,7 +13,6 @@ import javax.annotation.Nullable; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferConstraint; -import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor.api.model.RaptorConstrainedTransfer; import org.opentripplanner.raptor.api.model.RaptorStopNameResolver; import org.opentripplanner.raptor.api.model.RaptorTransfer; @@ -30,6 +29,7 @@ import org.opentripplanner.raptor.spi.RaptorTimeTable; import org.opentripplanner.raptor.spi.RaptorTransitDataProvider; import org.opentripplanner.raptor.util.BitSetIterator; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; import org.opentripplanner.routing.algorithm.raptoradapter.api.DefaultTripPattern; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultRaptorTransfer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.CostCalculatorFactory; @@ -38,6 +38,11 @@ import org.opentripplanner.routing.algorithm.transferoptimization.model.TripStopTime; import org.opentripplanner.routing.algorithm.transferoptimization.services.TransferServiceAdaptor; +/** + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated @SuppressWarnings("UnusedReturnValue") public class TestTransitData implements RaptorTransitDataProvider, RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripPattern.java similarity index 94% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripPattern.java index 811c17cc568..f6d9aace021 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripPattern.java @@ -1,9 +1,15 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import org.opentripplanner.routing.algorithm.raptoradapter.api.DefaultTripPattern; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.utils.tostring.ToStringBuilder; +/** + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated public class TestTripPattern implements DefaultTripPattern { public static final byte BOARDING_MASK = 0b0001; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSchedule.java similarity index 97% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSchedule.java index 75062d9a61e..0ef437cb3d0 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSchedule.java @@ -1,4 +1,4 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import static org.opentripplanner.transit.model.basic.Accessibility.NO_INFORMATION; @@ -19,8 +19,11 @@ * An implementation of the {@link RaptorTripSchedule} for unit-testing. *

    * The {@link DefaultTripPattern} for this schedule return {@code stopIndex == stopPosInPattern + 1 } + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. */ -// TODO : This class should implement RaptorTripSchedule not raptoradapter TripSchedule +@Deprecated public class TestTripSchedule implements TripSchedule { private static final int DEFAULT_DEPARTURE_DELAY = 10; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSearchTimetable.java b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSearchTimetable.java similarity index 86% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSearchTimetable.java rename to application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSearchTimetable.java index 0785e20c241..a486492a2c6 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSearchTimetable.java +++ b/application/src/test/java/org/opentripplanner/raptorlegacy/_data/transit/TestTripSearchTimetable.java @@ -1,4 +1,4 @@ -package org.opentripplanner.raptor._data.transit; +package org.opentripplanner.raptorlegacy._data.transit; import java.util.function.IntUnaryOperator; import org.opentripplanner.raptor.api.model.SearchDirection; @@ -6,6 +6,12 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripScheduleSearchFactory; import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TripSearchTimetable; +/** + * + * @deprecated This was earlier part of Raptor and should not be used outside the Raptor + * module. Use the OTP model entities instead. + */ +@Deprecated public class TestTripSearchTimetable implements TripSearchTimetable { private final TestTripSchedule[] trips; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/CarPickupSnapshotTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/CarPickupSnapshotTest.java new file mode 100644 index 00000000000..c233d0c6bbc --- /dev/null +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/CarPickupSnapshotTest.java @@ -0,0 +1,71 @@ +package org.opentripplanner.routing.algorithm.mapping; + +import au.com.origin.snapshots.junit5.SnapshotExtension; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opentripplanner.model.GenericLocation; +import org.opentripplanner.model.modes.ExcludeAllTransitFilter; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; + +@ExtendWith(SnapshotExtension.class) +public class CarPickupSnapshotTest extends SnapshotTestBase { + + static GenericLocation p0 = new GenericLocation( + "SE Stark St. & SE 17th Ave. (P0)", + null, + 45.519320, + -122.648567 + ); + + static GenericLocation p1 = new GenericLocation( + "SE Morrison St. & SE 17th Ave. (P1)", + null, + 45.51726, + -122.64847 + ); + + static GenericLocation p2 = new GenericLocation( + "NW Northrup St. & NW 22nd Ave. (P2)", + null, + 45.53122, + -122.69659 + ); + + @BeforeAll + public static void beforeClass() { + loadGraphBeforeClass(false); + } + + @Test + public void test_trip_planning_with_car_pickup_only() { + RouteRequest request = createTestRequest(2009, 11, 17, 10, 0, 0); + + request.journey().direct().setMode(StreetMode.CAR_PICKUP); + request.journey().transit().setFilters(List.of(ExcludeAllTransitFilter.of())); + + request.setFrom(p0); + request.setTo(p2); + + expectRequestResponseToMatchSnapshot(request); + } + + @Test + public void test_trip_planning_with_car_pickup_transfer() { + RouteRequest request = createTestRequest(2009, 11, 17, 10, 0, 0); + + request.journey().access().setMode(StreetMode.WALK); + request.journey().egress().setMode(StreetMode.WALK); + request.journey().direct().setMode(StreetMode.WALK); + request.journey().transfer().setMode(StreetMode.CAR_PICKUP); + request.journey().transit().setFilters(List.of(AllowAllTransitFilter.of())); + + request.setFrom(p0); + request.setTo(p2); + + expectRequestResponseToMatchSnapshot(request); + } +} diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/PagingServiceFactoryTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/PagingServiceFactoryTest.java index 64797b9c5a1..0eb7bfae1d3 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/PagingServiceFactoryTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/PagingServiceFactoryTest.java @@ -7,10 +7,10 @@ import java.time.Instant; import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.api.request.RaptorTuningParameters; import org.opentripplanner.raptor.api.request.SearchParams; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitTuningParameters; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.utils.time.DurationUtils; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index ede3166e22b..8f2fc45b875 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.raptor._data.RaptorTestConstants.BOARD_SLACK; +import static org.opentripplanner.raptorlegacy._data.RaptorTestConstants.BOARD_SLACK; import java.time.Duration; import java.time.Instant; @@ -29,13 +29,8 @@ import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.model.plan.StreetLeg; -import org.opentripplanner.raptor._data.api.TestPathBuilder; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestRoute; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.path.AccessPathLeg; @@ -45,6 +40,12 @@ import org.opentripplanner.raptor.api.path.TransferPathLeg; import org.opentripplanner.raptor.path.Path; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptorlegacy._data.api.TestPathBuilder; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestRoute; +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.router.street.AccessEgressType; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultAccessEgress; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultRaptorTransfer; @@ -52,7 +53,6 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.search.state.State; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarPickupSnapshotTest.snap b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarPickupSnapshotTest.snap new file mode 100644 index 00000000000..a3a515f82b4 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarPickupSnapshotTest.snap @@ -0,0 +1,3438 @@ +org.opentripplanner.routing.algorithm.mapping.CarPickupSnapshotTest.test_trip_planning_with_car_pickup_only=[ + [ + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 634, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:10:34.000+00:00", + "fare" : { + "details" : { }, + "fare" : { } + }, + "generalizedCost" : 1005, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 5050.5, + "endTime" : "2009-11-17T18:10:34.000+00:00", + "from" : { + "departure" : "2009-11-17T18:00:00.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 1005, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 200, + "points" : "unytGpxqkVA??dACpB@P?f@?p@?j@?dAAhE?jE?vD?PK?aC?{BAS?mC??hE?nEAfE?hE?hEAnC?^?Z?pB?J?~@?J}B?O?OAo@?A?w@?U?S?iB?O??Z?xC?XAJ?P?R@H@FBFBFBDDDBBDBF@F?d@@H@D@FBFD@BBDBF@H@L?L?b@@RBTGpJCjEA|CArAAxAAxAAdBEzHClF?@Ax@GTAjBAZQ?qBBuA@Y@[@aCDM@K?aCBCV@pCDZ@|D@N@vD?L?F?JBbD?D?N?BK?uBBM?K?uBBI@K?aBBSAM@M@K?GBEDEHIJUX_@f@KNQRSVGFCBQPIHGDGBIBK@W@a@?mA@E?C@C@A?CBQTKJEHIJeC~CYZo@v@g@d@IJAFAD?L@vC@hB@p@?X?T@P?N@P@nA?j@AX?L@`B@dA?R?RBbD?T?NBbD?R?P@|D@jB?x@@J?N?B?P?LBjD@N@fD?T?LBdD?R?PF`K?RDlJ?R?PFlJ?J" + }, + "mode" : "CAR", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:00:00.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 395.42, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 238.05, + "elevation" : "", + "lat" : 45.5193421, + "lon" : -122.6536397, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 12th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 553.35, + "elevation" : "", + "lat" : 45.5214829, + "lon" : -122.6536226, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Ash Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 158.48, + "elevation" : "", + "lat" : 45.5215062, + "lon" : -122.6607251, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast Grand Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 78.88, + "elevation" : "", + "lat" : 45.5229315, + "lon" : -122.6607174, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast Grand Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 168.84, + "elevation" : "", + "lat" : 45.5236409, + "lon" : -122.6607123, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northeast Couch Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "SOUTHWEST", + "area" : false, + "bogusName" : false, + "distance" : 64.13, + "elevation" : "", + "lat" : 45.5231423, + "lon" : -122.6623136, + "relativeDirection" : "SLIGHTLY_RIGHT", + "stayOn" : true, + "streetName" : "Northeast Couch Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 144.26, + "elevation" : "", + "lat" : 45.522964, + "lon" : -122.6630306, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "East Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 407.38, + "elevation" : "", + "lat" : 45.523001, + "lon" : -122.6648816, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Burnside Bridge", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 178.19, + "elevation" : "", + "lat" : 45.5231054, + "lon" : -122.6701087, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 310.7, + "elevation" : "", + "lat" : 45.5231958, + "lon" : -122.6723802, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 2nd Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 317.18, + "elevation" : "", + "lat" : 45.5259887, + "lon" : -122.6724927, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Flanders Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 238.16, + "elevation" : "", + "lat" : 45.5259105, + "lon" : -122.6765581, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 6th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 465.44, + "elevation" : "", + "lat" : 45.5280515, + "lon" : -122.6766232, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northwest Station Way", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 1331.38, + "elevation" : "", + "lat" : 45.5315452, + "lon" : -122.679511, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:10:34.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:00:00.000+00:00", + "tooSloped" : false, + "transfers" : 0, + "transitTime" : 0, + "waitingTime" : 0, + "walkDistance" : 5050.5, + "walkLimitExceeded" : false, + "walkTime" : 634 + } + ] +] + + +org.opentripplanner.routing.algorithm.mapping.CarPickupSnapshotTest.test_trip_planning_with_car_pickup_transfer=[ + [ + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 3804, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T19:03:24.000+00:00", + "fare" : { + "details" : { }, + "fare" : { } + }, + "generalizedCost" : 7384, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 4875.79, + "endTime" : "2009-11-17T19:03:24.000+00:00", + "from" : { + "departure" : "2009-11-17T18:00:00.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 7384, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 252, + "points" : "unytGpxqkVA??dACpB@P?f@?p@?j@?dAAhE?jE?vD?P?RK?GFCDCFADABADADAF?D?FADCF?B?B?@?D@DADABCBC?A?C?AA??GICAAAA?A?A?AACAC?A@ABABC@CDCBCBC@CBABABCJ?J?@?@MR?hE?fEAdB?dB?lE?`A?T?pBA|D?J?x@?r@y@??bBuA?y@AU?{@?O?}@?E?y@?_@Aa@?u@?W??J@N?hA@x@Ap@ATETGpJCjEA|CArAAxAAxAAdBEzHClF?@Ax@GTAjBAZ?R@hC@h@Q@cBBM?M?_CDsA@Y?Q@O?K?Q?mBB[?C?W@[?]@E?IDI@]DO@SBM?S@G@A?GDEJQ|@AL?L@nA?j@@L?V?d@@~@@NI?E?W@Q?Q?m@@Q@AF?DAJBzB?V?NANM@K?GBEDEHIJUX_@f@KNQRSVGFCBQPIHGDGBIBK@W@a@?mA@E?C@C@A?CBQTKJEHIJeC~CYZo@v@g@d@IJAFAD?L@vC@hB@p@?X?T@P?N@P@nA?j@AX?L@`B@dA?R?RBbD?T?NBbD?R?P@|D@jB?x@@J?N?B?P?LBjD@N@fD?T?LBdD?R?PF`K?RDlJ?R?PFlJ?J" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:00:00.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 402.49, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : true, + "distance" : 131.76, + "elevation" : "", + "lat" : 45.5193421, + "lon" : -122.6537305, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 516.5, + "elevation" : "", + "lat" : 45.5200623, + "lon" : -122.6546522, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Oak Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : true, + "distance" : 70.84, + "elevation" : "", + "lat" : 45.5200842, + "lon" : -122.6612814, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "parking aisle", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 284.75, + "elevation" : "", + "lat" : 45.5203736, + "lon" : -122.661783, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast Martin Luther King, Junior Boulevard", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 243.45, + "elevation" : "", + "lat" : 45.5229343, + "lon" : -122.6617665, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "East Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 407.38, + "elevation" : "", + "lat" : 45.523001, + "lon" : -122.6648816, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Burnside Bridge", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 256.85, + "elevation" : "", + "lat" : 45.5231054, + "lon" : -122.6701087, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 460.65, + "elevation" : "", + "lat" : 45.5231776, + "lon" : -122.6733895, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 3rd Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 145.82, + "elevation" : "", + "lat" : 45.5272866, + "lon" : -122.6737165, + "relativeDirection" : "SLIGHTLY_LEFT", + "stayOn" : false, + "streetName" : "Northwest Hoyt Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 77.35, + "elevation" : "", + "lat" : 45.5273496, + "lon" : -122.6755629, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 5th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 80.53, + "elevation" : "", + "lat" : 45.5280449, + "lon" : -122.6755935, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Irving Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 465.44, + "elevation" : "", + "lat" : 45.5280515, + "lon" : -122.6766232, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest Station Way", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 1331.38, + "elevation" : "", + "lat" : 45.5315452, + "lon" : -122.679511, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T19:03:24.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:00:00.000+00:00", + "tooSloped" : false, + "transfers" : 0, + "transitTime" : 0, + "waitingTime" : 0, + "walkDistance" : 4875.79, + "walkLimitExceeded" : false, + "walkTime" : 3804 + }, + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 1815, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:34:19.000+00:00", + "fare" : { + "details" : { }, + "fare" : { }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 5 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] + }, + "generalizedCost" : 3832, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 673.56, + "endTime" : "2009-11-17T18:12:58.000+00:00", + "from" : { + "departure" : "2009-11-17T18:04:04.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 1031, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 42, + "points" : "unytGpxqkVA??dACpB@PoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:04:04.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 79.12, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 402.13, + "elevation" : "", + "lat" : 45.5193388, + "lon" : -122.6495798, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 168.89, + "elevation" : "", + "lat" : 45.5228912, + "lon" : -122.6495528, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 22.74, + "elevation" : "", + "lat" : 45.524409, + "lon" : -122.6495675, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northeast Sandy Boulevard", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 2119.06, + "endTime" : "2009-11-17T18:19:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "stopIndex" : 92, + "stopSequence" : 93, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 962, + "headsign" : "Beaverton TC", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:13:32.000+00:00", + "departure" : "2009-11-17T18:13:32.000+00:00", + "lat" : 45.523767, + "lon" : -122.651428, + "name" : "NE Sandy & 14th", + "stopCode" : "5058", + "stopId" : "prt:5058", + "stopIndex" : 93, + "stopSequence" : 94, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:14:00.000+00:00", + "departure" : "2009-11-17T18:14:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:14:47.000+00:00", + "departure" : "2009-11-17T18:14:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:15:24.000+00:00", + "departure" : "2009-11-17T18:15:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:15:52.000+00:00", + "departure" : "2009-11-17T18:15:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:18:00.000+00:00", + "departure" : "2009-11-17T18:18:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, + "vertexType" : "TRANSIT", + "zoneId" : "0" + } + ], + "legGeometry" : { + "length" : 50, + "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:12:58.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : true, + "tripBlockId" : "2002", + "tripId" : "prt:200W1200" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 0.0, + "endTime" : "2009-11-17T18:19:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1, + "interlineWithPreviousLeg" : false, + "legElevation" : "", + "legGeometry" : { + "length" : 2, + "points" : "wfztGjcwkVD?" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:19:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.5231324, + "lon" : -122.6758917, + "name" : "West Burnside Street", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 1767.93, + "endTime" : "2009-11-17T18:24:16.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.5231324, + "lon" : -122.6758917, + "name" : "West Burnside Street", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 510, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 97, + "points" : "qfztGjcwkV@`B?H?BM?iBBI?K?wBBK?K?sBDM@M@sB@K?K?uBBM?K?uBBI@K?aBBSAM@M@K?GBEDEHIJUX_@f@KNQRSVGFCBQPIHGDGBIBK@W@a@?mA@E?C@C@A?CBQTKJEHIJeC~CYZo@v@g@d@IJAFAD?L@vC@hB@p@?X?T@P?N@P@nA?j@AX?L@`B@dA?R?RBbD?T?NBbD?R?P@|D@jB?x@@J?N?B?P?LBjD@N" + }, + "mode" : "CAR", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:19:00.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 44.21, + "elevation" : "", + "lat" : 45.5231324, + "lon" : -122.6758917, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 548.38, + "elevation" : "", + "lat" : 45.5231221, + "lon" : -122.676459, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 6th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 465.44, + "elevation" : "", + "lat" : 45.5280515, + "lon" : -122.6766232, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northwest Station Way", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 709.95, + "elevation" : "", + "lat" : 45.5315452, + "lon" : -122.679511, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:24:16.000+00:00", + "departure" : "2009-11-17T18:24:16.000+00:00", + "lat" : 45.5313992, + "lon" : -122.6886162, + "name" : "corner of Northwest Northrup Street and path", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 70.46, + "endTime" : "2009-11-17T18:26:18.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:24:16.000+00:00", + "departure" : "2009-11-17T18:24:16.000+00:00", + "lat" : 45.5313992, + "lon" : -122.6886162, + "name" : "corner of Northwest Northrup Street and path", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 233, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "ez{tGzrykVC?I?@nBBH?d@??" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:24:16.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : true, + "distance" : 7.61, + "elevation" : "", + "lat" : 45.5313992, + "lon" : -122.6886162, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 62.85, + "elevation" : "", + "lat" : 45.5314676, + "lon" : -122.6886182, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:26:18.000+00:00", + "departure" : "2009-11-17T18:31:26.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 547.79, + "endTime" : "2009-11-17T18:33:55.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:26:18.000+00:00", + "departure" : "2009-11-17T18:31:26.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 20, + "stopSequence" : 21, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 1057, + "headsign" : "NW 23rd Ave", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:33:13.000+00:00", + "departure" : "2009-11-17T18:33:13.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 21, + "stopSequence" : 22, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 8, + "points" : "cz{tGzwykV?VBhEFtKDvJ??@\\DnJ" + }, + "mode" : "TRAM", + "pathway" : false, + "realTime" : false, + "route" : "Portland Streetcar", + "routeId" : "prt:193", + "routeLongName" : "Portland Streetcar", + "routeType" : 0, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:31:26.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:33:55.000+00:00", + "departure" : "2009-11-17T18:33:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 22, + "stopSequence" : 23, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "9384", + "tripId" : "prt:1930W1210" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 18.81, + "endTime" : "2009-11-17T18:34:19.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:33:55.000+00:00", + "departure" : "2009-11-17T18:33:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 37, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "sy{tGxc{kV???LABF?B??J" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:33:55.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 18.81, + "elevation" : "", + "lat" : 45.5313019, + "lon" : -122.6964448, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:34:19.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:04:04.000+00:00", + "tooSloped" : false, + "transfers" : 1, + "transitTime" : 511, + "waitingTime" : 308, + "walkDistance" : 2530.76, + "walkLimitExceeded" : false, + "walkTime" : 996 + }, + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 1598, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:35:19.000+00:00", + "fare" : { + "details" : { }, + "fare" : { }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 5 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] + }, + "generalizedCost" : 3378, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 408.65, + "endTime" : "2009-11-17T18:14:00.000+00:00", + "from" : { + "departure" : "2009-11-17T18:08:41.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 623, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 16, + "points" : "unytGpxqkVA??dACpB@P?f@?p@?j@?dAAhE?jE?vD?PJ?J@?S" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:08:41.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 395.42, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 12.54, + "elevation" : "", + "lat" : 45.5193421, + "lon" : -122.6536397, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast 12th Avenue", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:14:00.000+00:00", + "departure" : "2009-11-17T18:14:00.000+00:00", + "lat" : 45.519229, + "lon" : -122.653546, + "name" : "SE 12th & Stark", + "stopCode" : "6594", + "stopId" : "prt:6594", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 439.45, + "endTime" : "2009-11-17T18:16:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:14:00.000+00:00", + "departure" : "2009-11-17T18:14:00.000+00:00", + "lat" : 45.519229, + "lon" : -122.653546, + "name" : "SE 12th & Stark", + "stopCode" : "6594", + "stopId" : "prt:6594", + "stopIndex" : 32, + "stopSequence" : 33, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 720, + "headsign" : "Rose Qtr TC", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:14:44.000+00:00", + "departure" : "2009-11-17T18:14:44.000+00:00", + "lat" : 45.520674, + "lon" : -122.653544, + "name" : "SE 12th & Pine", + "stopCode" : "6589", + "stopId" : "prt:6589", + "stopIndex" : 33, + "stopSequence" : 34, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 11, + "points" : "cnytGdxrkVW?mC?{BA??Q?oC?mC?kBAa@?w@?" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "12th Ave", + "routeId" : "prt:70", + "routeLongName" : "12th Ave", + "routeShortName" : "70", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:14:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:16:00.000+00:00", + "departure" : "2009-11-17T18:16:00.000+00:00", + "lat" : 45.52318, + "lon" : -122.653507, + "name" : "NE 12th & Sandy", + "stopCode" : "6592", + "stopId" : "prt:6592", + "stopIndex" : 34, + "stopSequence" : 35, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "7004", + "tripId" : "prt:700W1150" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 0.0, + "endTime" : "2009-11-17T18:16:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:16:00.000+00:00", + "departure" : "2009-11-17T18:16:00.000+00:00", + "lat" : 45.52318, + "lon" : -122.653507, + "name" : "NE 12th & Sandy", + "stopCode" : "6592", + "stopId" : "prt:6592", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 1, + "interlineWithPreviousLeg" : false, + "legElevation" : "", + "legGeometry" : { + "length" : 2, + "points" : "{fztGlwrkV?V" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:16:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:16:00.000+00:00", + "departure" : "2009-11-17T18:16:00.000+00:00", + "lat" : 45.5231819, + "lon" : -122.6536285, + "name" : "Northeast 12th Avenue", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 1607.0, + "endTime" : "2009-11-17T18:21:03.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:16:00.000+00:00", + "departure" : "2009-11-17T18:16:00.000+00:00", + "lat" : 45.5231819, + "lon" : -122.6536285, + "name" : "Northeast 12th Avenue", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 473, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 81, + "points" : "{fztGdxrkVc@?C?_@AM?O?oBAO??jE?hEAhE?jE?hEAjE?lD?R?FO?]?qA?O?UBmE?k@EU??X?tC?X?P?HAjG?j@ARANCRG\\KFM\\IXCPKXELCJAHMd@?HM\\O`@KPKNKHMLKFIFYJk@P]JIBM@M@O@O?W@O?O?O?q@?Q@K@IBKDIFGFEFGLSFGHEHKZELGP" + }, + "mode" : "CAR", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:16:00.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 128.28, + "elevation" : "", + "lat" : 45.5231819, + "lon" : -122.6536285, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northeast 12th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 553.03, + "elevation" : "", + "lat" : 45.5243354, + "lon" : -122.6536083, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northeast Davis Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 243.39, + "elevation" : "", + "lat" : 45.5243542, + "lon" : -122.6607071, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northeast Grand Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 542.09, + "elevation" : "", + "lat" : 45.5265408, + "lon" : -122.6606919, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northeast Lloyd Boulevard", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 140.22, + "elevation" : "", + "lat" : 45.5286604, + "lon" : -122.6657552, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "North Interstate Avenue", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:21:03.000+00:00", + "departure" : "2009-11-17T18:21:03.000+00:00", + "lat" : 45.5297183, + "lon" : -122.6664542, + "name" : "corner of North Interstate Avenue and path", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 43.34, + "endTime" : "2009-11-17T18:22:49.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:21:03.000+00:00", + "departure" : "2009-11-17T18:21:03.000+00:00", + "lat" : 45.5297183, + "lon" : -122.6664542, + "name" : "corner of North Interstate Avenue and path", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 196, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 13, + "points" : "uo{tGjhukVKIC?CDABCDABEFCHGCEEGK@A" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:21:03.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : true, + "distance" : 28.77, + "elevation" : "", + "lat" : 45.5297183, + "lon" : -122.6664542, + "relativeDirection" : "SLIGHTLY_RIGHT", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 14.56, + "elevation" : "", + "lat" : 45.5299086, + "lon" : -122.6665929, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Rose Quarter Transit Center", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:22:49.000+00:00", + "departure" : "2009-11-17T18:25:00.000+00:00", + "lat" : 45.530005, + "lon" : -122.666476, + "name" : "Rose Quarter Transit Center", + "stopCode" : "2592", + "stopId" : "prt:2592", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 2905.12, + "endTime" : "2009-11-17T18:34:55.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:22:49.000+00:00", + "departure" : "2009-11-17T18:25:00.000+00:00", + "lat" : 45.530005, + "lon" : -122.666476, + "name" : "Rose Quarter Transit Center", + "stopCode" : "2592", + "stopId" : "prt:2592", + "stopIndex" : 84, + "stopSequence" : 85, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1326, + "headsign" : "Montgomery Park", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:28:20.000+00:00", + "departure" : "2009-11-17T18:28:20.000+00:00", + "lat" : 45.526655, + "lon" : -122.676462, + "name" : "NW Glisan & 6th", + "stopCode" : "10803", + "stopId" : "prt:10803", + "stopIndex" : 85, + "stopSequence" : 86, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:29:15.000+00:00", + "departure" : "2009-11-17T18:29:15.000+00:00", + "lat" : 45.528799, + "lon" : -122.677238, + "name" : "NW Station Way & Union Station", + "stopCode" : "12801", + "stopId" : "prt:12801", + "stopIndex" : 86, + "stopSequence" : 87, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:31:00.000+00:00", + "departure" : "2009-11-17T18:31:00.000+00:00", + "lat" : 45.531582, + "lon" : -122.681193, + "name" : "NW Northrup & 10th", + "stopCode" : "12802", + "stopId" : "prt:12802", + "stopIndex" : 87, + "stopSequence" : 88, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:31:33.000+00:00", + "departure" : "2009-11-17T18:31:33.000+00:00", + "lat" : 45.531534, + "lon" : -122.683319, + "name" : "NW 12th & Northrup", + "stopCode" : "12796", + "stopId" : "prt:12796", + "stopIndex" : 88, + "stopSequence" : 89, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:32:04.000+00:00", + "departure" : "2009-11-17T18:32:04.000+00:00", + "lat" : 45.531503, + "lon" : -122.685357, + "name" : "NW Northrup & 14th", + "stopCode" : "10775", + "stopId" : "prt:10775", + "stopIndex" : 89, + "stopSequence" : 90, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:33:07.000+00:00", + "departure" : "2009-11-17T18:33:07.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 90, + "stopSequence" : 91, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:34:24.000+00:00", + "departure" : "2009-11-17T18:34:24.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 91, + "stopSequence" : 92, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 76, + "points" : "eq{tG`hukVNXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Broadway/Halsey", + "routeId" : "prt:77", + "routeLongName" : "Broadway/Halsey", + "routeShortName" : "77", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:25:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:34:55.000+00:00", + "departure" : "2009-11-17T18:34:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 92, + "stopSequence" : 93, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "7736", + "tripId" : "prt:771W1160" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 18.81, + "endTime" : "2009-11-17T18:35:19.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:34:55.000+00:00", + "departure" : "2009-11-17T18:34:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 37, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "sy{tGxc{kV???LABF?B??J" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:34:55.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 18.81, + "elevation" : "", + "lat" : 45.5313019, + "lon" : -122.6964448, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:35:19.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:08:41.000+00:00", + "tooSloped" : false, + "transfers" : 1, + "transitTime" : 715, + "waitingTime" : 131, + "walkDistance" : 2077.8, + "walkLimitExceeded" : false, + "walkTime" : 752 + }, + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 2077, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:38:41.000+00:00", + "fare" : { + "details" : { }, + "fare" : { }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] + }, + "generalizedCost" : 3914, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 673.56, + "endTime" : "2009-11-17T18:12:58.000+00:00", + "from" : { + "departure" : "2009-11-17T18:04:04.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 1031, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 42, + "points" : "unytGpxqkVA??dACpB@PoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:04:04.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 79.12, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 402.13, + "elevation" : "", + "lat" : 45.5193388, + "lon" : -122.6495798, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 168.89, + "elevation" : "", + "lat" : 45.5228912, + "lon" : -122.6495528, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 22.74, + "elevation" : "", + "lat" : 45.524409, + "lon" : -122.6495675, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northeast Sandy Boulevard", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 3602.73, + "endTime" : "2009-11-17T18:25:49.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "stopIndex" : 92, + "stopSequence" : 93, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 1371, + "headsign" : "Beaverton TC", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:13:32.000+00:00", + "departure" : "2009-11-17T18:13:32.000+00:00", + "lat" : 45.523767, + "lon" : -122.651428, + "name" : "NE Sandy & 14th", + "stopCode" : "5058", + "stopId" : "prt:5058", + "stopIndex" : 93, + "stopSequence" : 94, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:14:00.000+00:00", + "departure" : "2009-11-17T18:14:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:14:47.000+00:00", + "departure" : "2009-11-17T18:14:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:15:24.000+00:00", + "departure" : "2009-11-17T18:15:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:15:52.000+00:00", + "departure" : "2009-11-17T18:15:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:18:00.000+00:00", + "departure" : "2009-11-17T18:18:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:20:17.000+00:00", + "departure" : "2009-11-17T18:20:17.000+00:00", + "lat" : 45.523115, + "lon" : -122.678939, + "name" : "W Burnside & NW Park", + "stopCode" : "716", + "stopId" : "prt:716", + "stopIndex" : 100, + "stopSequence" : 101, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:21:25.000+00:00", + "departure" : "2009-11-17T18:21:25.000+00:00", + "lat" : 45.523048, + "lon" : -122.681606, + "name" : "W Burnside & NW 10th", + "stopCode" : "10791", + "stopId" : "prt:10791", + "stopIndex" : 101, + "stopSequence" : 102, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:22:14.000+00:00", + "departure" : "2009-11-17T18:22:14.000+00:00", + "lat" : 45.523, + "lon" : -122.683535, + "name" : "W Burnside & NW 12th", + "stopCode" : "11032", + "stopId" : "prt:11032", + "stopIndex" : 102, + "stopSequence" : 103, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:24:09.000+00:00", + "departure" : "2009-11-17T18:24:09.000+00:00", + "lat" : 45.522985, + "lon" : -122.688091, + "name" : "W Burnside & NW 17th", + "stopCode" : "10809", + "stopId" : "prt:10809", + "stopIndex" : 103, + "stopSequence" : 104, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:25:00.000+00:00", + "departure" : "2009-11-17T18:25:00.000+00:00", + "lat" : 45.523097, + "lon" : -122.690083, + "name" : "W Burnside & NW 19th", + "stopCode" : "735", + "stopId" : "prt:735", + "stopIndex" : 104, + "stopSequence" : 105, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:25:21.000+00:00", + "departure" : "2009-11-17T18:25:21.000+00:00", + "lat" : 45.523176, + "lon" : -122.692139, + "name" : "W Burnside & NW 20th", + "stopCode" : "741", + "stopId" : "prt:741", + "stopIndex" : 105, + "stopSequence" : 106, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:25:31.000+00:00", + "departure" : "2009-11-17T18:25:31.000+00:00", + "lat" : 45.52322, + "lon" : -122.69313, + "name" : "W Burnside & NW 20th Pl", + "stopCode" : "742", + "stopId" : "prt:742", + "stopIndex" : 106, + "stopSequence" : 107, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 94, + "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:12:58.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:25:49.000+00:00", + "departure" : "2009-11-17T18:25:49.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 107, + "stopSequence" : 108, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "2002", + "tripId" : "prt:200W1200" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 999.1, + "endTime" : "2009-11-17T18:38:41.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:25:49.000+00:00", + "departure" : "2009-11-17T18:25:49.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 1511, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 29, + "points" : "ugztGdzzkVL?ATClAI|DK?G?mCBkCDoCDmCBoCDkCBoCB[?sBD]?Y@eA@K?C?K?W@{A@M@C@I?_CB?G" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:25:49.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 113.27, + "elevation" : "", + "lat" : 45.5232491, + "lon" : -122.6949067, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 882.16, + "elevation" : "", + "lat" : 45.5233204, + "lon" : -122.696357, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 22nd Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "EAST", + "area" : false, + "bogusName" : false, + "distance" : 3.68, + "elevation" : "", + "lat" : 45.5312508, + "lon" : -122.6966386, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:38:41.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:04:04.000+00:00", + "tooSloped" : false, + "transfers" : 0, + "transitTime" : 771, + "waitingTime" : 0, + "walkDistance" : 1672.66, + "walkLimitExceeded" : false, + "walkTime" : 1306 + }, + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 1646, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:39:22.000+00:00", + "fare" : { + "details" : { }, + "fare" : { }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] + }, + "generalizedCost" : 2662, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 290.72, + "endTime" : "2009-11-17T18:15:40.000+00:00", + "from" : { + "departure" : "2009-11-17T18:11:56.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 442, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 6, + "points" : "unytGpxqkVjC?lC@nC@?fCG?" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:11:56.000+00:00", + "steps" : [ + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 237.26, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 53.47, + "elevation" : "", + "lat" : 45.5171863, + "lon" : -122.6485801, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast Morrison Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:15:40.000+00:00", + "departure" : "2009-11-17T18:15:40.000+00:00", + "lat" : 45.517226, + "lon" : -122.649266, + "name" : "SE Morrison & 16th", + "stopCode" : "4019", + "stopId" : "prt:4019", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 5218.86, + "endTime" : "2009-11-17T18:35:54.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:15:40.000+00:00", + "departure" : "2009-11-17T18:15:40.000+00:00", + "lat" : 45.517226, + "lon" : -122.649266, + "name" : "SE Morrison & 16th", + "stopCode" : "4019", + "stopId" : "prt:4019", + "stopIndex" : 50, + "stopSequence" : 51, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 1814, + "headsign" : "Montgomery Park", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:16:15.000+00:00", + "departure" : "2009-11-17T18:16:15.000+00:00", + "lat" : 45.517253, + "lon" : -122.651354, + "name" : "SE Morrison & 14th", + "stopCode" : "4016", + "stopId" : "prt:4016", + "stopIndex" : 51, + "stopSequence" : 52, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:17:00.000+00:00", + "departure" : "2009-11-17T18:17:00.000+00:00", + "lat" : 45.517299, + "lon" : -122.654067, + "name" : "SE Morrison & 12th", + "stopCode" : "4014", + "stopId" : "prt:4014", + "stopIndex" : 52, + "stopSequence" : 53, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:17:38.000+00:00", + "departure" : "2009-11-17T18:17:38.000+00:00", + "lat" : 45.517292, + "lon" : -122.656563, + "name" : "SE Morrison & 9th", + "stopCode" : "4026", + "stopId" : "prt:4026", + "stopIndex" : 53, + "stopSequence" : 54, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:18:08.000+00:00", + "departure" : "2009-11-17T18:18:08.000+00:00", + "lat" : 45.517322, + "lon" : -122.65847, + "name" : "SE Morrison & 7th", + "stopCode" : "4025", + "stopId" : "prt:4025", + "stopIndex" : 54, + "stopSequence" : 55, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:18:39.000+00:00", + "departure" : "2009-11-17T18:18:39.000+00:00", + "lat" : 45.517298, + "lon" : -122.660523, + "name" : "SE Morrison & Grand", + "stopCode" : "4013", + "stopId" : "prt:4013", + "stopIndex" : 55, + "stopSequence" : 56, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:20:03.000+00:00", + "departure" : "2009-11-17T18:20:03.000+00:00", + "lat" : 45.517351, + "lon" : -122.66601, + "name" : "Morrison Bridge", + "stopCode" : "4029", + "stopId" : "prt:4029", + "stopIndex" : 56, + "stopSequence" : 57, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:22:27.000+00:00", + "departure" : "2009-11-17T18:22:27.000+00:00", + "lat" : 45.51959, + "lon" : -122.674599, + "name" : "SW Washington & 3rd", + "stopCode" : "6158", + "stopId" : "prt:6158", + "stopIndex" : 57, + "stopSequence" : 58, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:23:00.000+00:00", + "departure" : "2009-11-17T18:23:00.000+00:00", + "lat" : 45.520129, + "lon" : -122.676635, + "name" : "SW Washington & 5th", + "stopCode" : "6160", + "stopId" : "prt:6160", + "stopIndex" : 58, + "stopSequence" : 59, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:23:52.000+00:00", + "departure" : "2009-11-17T18:23:52.000+00:00", + "lat" : 45.520695, + "lon" : -122.678657, + "name" : "SW Washington & Broadway", + "stopCode" : "6137", + "stopId" : "prt:6137", + "stopIndex" : 59, + "stopSequence" : 60, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:24:34.000+00:00", + "departure" : "2009-11-17T18:24:34.000+00:00", + "lat" : 45.521124, + "lon" : -122.6803, + "name" : "SW Washington & 9th", + "stopCode" : "6169", + "stopId" : "prt:6169", + "stopIndex" : 60, + "stopSequence" : 61, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:25:47.000+00:00", + "departure" : "2009-11-17T18:25:47.000+00:00", + "lat" : 45.521094, + "lon" : -122.682819, + "name" : "SW 11th & Alder", + "stopCode" : "9600", + "stopId" : "prt:9600", + "stopIndex" : 61, + "stopSequence" : 62, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:26:36.000+00:00", + "departure" : "2009-11-17T18:26:36.000+00:00", + "lat" : 45.52055, + "lon" : -122.683933, + "name" : "SW Morrison & 12th", + "stopCode" : "9598", + "stopId" : "prt:9598", + "stopIndex" : 62, + "stopSequence" : 63, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:27:25.000+00:00", + "departure" : "2009-11-17T18:27:25.000+00:00", + "lat" : 45.521063, + "lon" : -122.685848, + "name" : "SW Morrison & 14th", + "stopCode" : "9708", + "stopId" : "prt:9708", + "stopIndex" : 63, + "stopSequence" : 64, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:28:18.000+00:00", + "departure" : "2009-11-17T18:28:18.000+00:00", + "lat" : 45.521641, + "lon" : -122.687932, + "name" : "SW Morrison & 16th", + "stopCode" : "9613", + "stopId" : "prt:9613", + "stopIndex" : 64, + "stopSequence" : 65, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:00.000+00:00", + "departure" : "2009-11-17T18:29:00.000+00:00", + "lat" : 45.52206, + "lon" : -122.689577, + "name" : "SW Morrison & 17th", + "stopCode" : "9599", + "stopId" : "prt:9599", + "stopIndex" : 65, + "stopSequence" : 66, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:54.000+00:00", + "departure" : "2009-11-17T18:29:54.000+00:00", + "lat" : 45.523097, + "lon" : -122.690083, + "name" : "W Burnside & NW 19th", + "stopCode" : "735", + "stopId" : "prt:735", + "stopIndex" : 66, + "stopSequence" : 67, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:30:31.000+00:00", + "departure" : "2009-11-17T18:30:31.000+00:00", + "lat" : 45.523176, + "lon" : -122.692139, + "name" : "W Burnside & NW 20th", + "stopCode" : "741", + "stopId" : "prt:741", + "stopIndex" : 67, + "stopSequence" : 68, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:30:48.000+00:00", + "departure" : "2009-11-17T18:30:48.000+00:00", + "lat" : 45.52322, + "lon" : -122.69313, + "name" : "W Burnside & NW 20th Pl", + "stopCode" : "742", + "stopId" : "prt:742", + "stopIndex" : 68, + "stopSequence" : 69, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:31:20.000+00:00", + "departure" : "2009-11-17T18:31:20.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 69, + "stopSequence" : 70, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:32:18.000+00:00", + "departure" : "2009-11-17T18:32:18.000+00:00", + "lat" : 45.523512, + "lon" : -122.698081, + "name" : "W Burnside & NW 23rd", + "stopCode" : "755", + "stopId" : "prt:755", + "stopIndex" : 70, + "stopSequence" : 71, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:33:11.000+00:00", + "departure" : "2009-11-17T18:33:11.000+00:00", + "lat" : 45.525416, + "lon" : -122.698381, + "name" : "NW 23rd & Flanders", + "stopCode" : "7157", + "stopId" : "prt:7157", + "stopIndex" : 71, + "stopSequence" : 72, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:34:05.000+00:00", + "departure" : "2009-11-17T18:34:05.000+00:00", + "lat" : 45.527543, + "lon" : -122.698473, + "name" : "NW 23rd & Irving", + "stopCode" : "7161", + "stopId" : "prt:7161", + "stopIndex" : 72, + "stopSequence" : 73, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:35:00.000+00:00", + "departure" : "2009-11-17T18:35:00.000+00:00", + "lat" : 45.529681, + "lon" : -122.698529, + "name" : "NW 23rd & Lovejoy", + "stopCode" : "7163", + "stopId" : "prt:7163", + "stopIndex" : 73, + "stopSequence" : 74, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 135, + "points" : "kaytG||qkVA~@?jE?tC???r@AhE?jE?rA???tBAjE?nD???X?hE?xC??Ah@?pE?~C???J?`@?vAAvBEbE?jEAlE?`BAbB@d@??@tAAj@Cx@Cb@Cp@_@dEcAtFoA`IS~@i@`BmAzDi@zAc@pAi@~C??Id@u@jEm@bD??If@u@jEk@bD??If@u@|DW`B??CPs@|Du@lElBz@??VJbCfAk@dD??Id@w@rEWvAId@AF??Q~@s@`Ei@~C??Ib@u@dEWzA??]jB]MQSe@WOKOKIIQe@GWE]GnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APkAh@o@?sCB{BD??S?mCDmCDyBB??U?mCDmCDyBB??S?oCDmCDmCBo@@" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Belmont/NW 23rd", + "routeId" : "prt:15", + "routeLongName" : "Belmont/NW 23rd", + "routeShortName" : "15", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:15:40.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:35:54.000+00:00", + "departure" : "2009-11-17T18:35:54.000+00:00", + "lat" : 45.532159, + "lon" : -122.698634, + "name" : "NW 23rd & Overton", + "stopCode" : "8981", + "stopId" : "prt:8981", + "stopIndex" : 74, + "stopSequence" : 75, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "1549", + "tripId" : "prt:150W1400" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 266.21, + "endTime" : "2009-11-17T18:39:22.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:35:54.000+00:00", + "departure" : "2009-11-17T18:35:54.000+00:00", + "lat" : 45.532159, + "lon" : -122.698634, + "name" : "NW 23rd & Overton", + "stopCode" : "8981", + "stopId" : "prt:8981", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 405, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 13, + "points" : "}~{tGnq{kV?LVAF?J?L?rBCLA?Q?EAyAEcH?G" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:35:54.000+00:00", + "steps" : [ + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 104.46, + "elevation" : "", + "lat" : 45.5321578, + "lon" : -122.6987026, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northwest 23rd Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "EAST", + "area" : false, + "bogusName" : false, + "distance" : 161.77, + "elevation" : "", + "lat" : 45.5312188, + "lon" : -122.6986675, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:39:22.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:11:56.000+00:00", + "tooSloped" : false, + "transfers" : 0, + "transitTime" : 1214, + "waitingTime" : 0, + "walkDistance" : 556.93, + "walkLimitExceeded" : false, + "walkTime" : 432 + }, + { + "arrivedAtDestinationWithRentedBicycle" : false, + "duration" : 1635, + "elevationGained" : 0.0, + "elevationLost" : 0.0, + "endTime" : "2009-11-17T18:46:19.000+00:00", + "fare" : { + "details" : { }, + "fare" : { }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 5 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] + }, + "generalizedCost" : 3652, + "legs" : [ + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 673.56, + "endTime" : "2009-11-17T18:27:58.000+00:00", + "from" : { + "departure" : "2009-11-17T18:19:04.000+00:00", + "lat" : 45.51932, + "lon" : -122.648567, + "name" : "SE Stark St. & SE 17th Ave. (P0)", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 1031, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 42, + "points" : "unytGpxqkVA??dACpB@PoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:19:04.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 0.69, + "elevation" : "", + "lat" : 45.51932, + "lon" : -122.6485648, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southeast 17th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 79.12, + "elevation" : "", + "lat" : 45.5193262, + "lon" : -122.6485648, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southeast Stark Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 402.13, + "elevation" : "", + "lat" : 45.5193388, + "lon" : -122.6495798, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 168.89, + "elevation" : "", + "lat" : 45.5228912, + "lon" : -122.6495528, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 22.74, + "elevation" : "", + "lat" : 45.524409, + "lon" : -122.6495675, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northeast Sandy Boulevard", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:27:58.000+00:00", + "departure" : "2009-11-17T18:27:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 2119.06, + "endTime" : "2009-11-17T18:34:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:27:58.000+00:00", + "departure" : "2009-11-17T18:27:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "stopIndex" : 92, + "stopSequence" : 93, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 962, + "headsign" : "23rd Ave to Tichner", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:28:32.000+00:00", + "departure" : "2009-11-17T18:28:32.000+00:00", + "lat" : 45.523767, + "lon" : -122.651428, + "name" : "NE Sandy & 14th", + "stopCode" : "5058", + "stopId" : "prt:5058", + "stopIndex" : 93, + "stopSequence" : 94, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:00.000+00:00", + "departure" : "2009-11-17T18:29:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:47.000+00:00", + "departure" : "2009-11-17T18:29:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:30:24.000+00:00", + "departure" : "2009-11-17T18:30:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:30:52.000+00:00", + "departure" : "2009-11-17T18:30:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:33:00.000+00:00", + "departure" : "2009-11-17T18:33:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, + "vertexType" : "TRANSIT", + "zoneId" : "0" + } + ], + "legGeometry" : { + "length" : 50, + "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:27:58.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : true, + "tripBlockId" : "2071", + "tripId" : "prt:200W1210" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 0.0, + "endTime" : "2009-11-17T18:34:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1, + "interlineWithPreviousLeg" : false, + "legElevation" : "", + "legGeometry" : { + "length" : 2, + "points" : "wfztGjcwkVD?" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:34:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.5231324, + "lon" : -122.6758917, + "name" : "West Burnside Street", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 1767.93, + "endTime" : "2009-11-17T18:39:16.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.5231324, + "lon" : -122.6758917, + "name" : "West Burnside Street", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 510, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 97, + "points" : "qfztGjcwkV@`B?H?BM?iBBI?K?wBBK?K?sBDM@M@sB@K?K?uBBM?K?uBBI@K?aBBSAM@M@K?GBEDEHIJUX_@f@KNQRSVGFCBQPIHGDGBIBK@W@a@?mA@E?C@C@A?CBQTKJEHIJeC~CYZo@v@g@d@IJAFAD?L@vC@hB@p@?X?T@P?N@P@nA?j@AX?L@`B@dA?R?RBbD?T?NBbD?R?P@|D@jB?x@@J?N?B?P?LBjD@N" + }, + "mode" : "CAR", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:34:00.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 44.21, + "elevation" : "", + "lat" : 45.5231324, + "lon" : -122.6758917, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 548.38, + "elevation" : "", + "lat" : 45.5231221, + "lon" : -122.676459, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 6th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 465.44, + "elevation" : "", + "lat" : 45.5280515, + "lon" : -122.6766232, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northwest Station Way", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 709.95, + "elevation" : "", + "lat" : 45.5315452, + "lon" : -122.679511, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:39:16.000+00:00", + "departure" : "2009-11-17T18:39:16.000+00:00", + "lat" : 45.5313992, + "lon" : -122.6886162, + "name" : "corner of Northwest Northrup Street and path", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 70.46, + "endTime" : "2009-11-17T18:41:18.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:39:16.000+00:00", + "departure" : "2009-11-17T18:39:16.000+00:00", + "lat" : 45.5313992, + "lon" : -122.6886162, + "name" : "corner of Northwest Northrup Street and path", + "vertexType" : "NORMAL" + }, + "generalizedCost" : 233, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "ez{tGzrykVC?I?@nBBH?d@??" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:39:16.000+00:00", + "steps" : [ + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : true, + "distance" : 7.61, + "elevation" : "", + "lat" : 45.5313992, + "lon" : -122.6886162, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 62.85, + "elevation" : "", + "lat" : 45.5314676, + "lon" : -122.6886182, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:41:18.000+00:00", + "departure" : "2009-11-17T18:43:26.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 547.79, + "endTime" : "2009-11-17T18:45:55.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:41:18.000+00:00", + "departure" : "2009-11-17T18:43:26.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 20, + "stopSequence" : 21, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 877, + "headsign" : "NW 23rd Ave", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:45:13.000+00:00", + "departure" : "2009-11-17T18:45:13.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 21, + "stopSequence" : 22, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 8, + "points" : "cz{tGzwykV?VBhEFtKDvJ??@\\DnJ" + }, + "mode" : "TRAM", + "pathway" : false, + "realTime" : false, + "route" : "Portland Streetcar", + "routeId" : "prt:193", + "routeLongName" : "Portland Streetcar", + "routeType" : 0, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:43:26.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:45:55.000+00:00", + "departure" : "2009-11-17T18:45:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 22, + "stopSequence" : 23, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "9385", + "tripId" : "prt:1930W1220" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 18.81, + "endTime" : "2009-11-17T18:46:19.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:45:55.000+00:00", + "departure" : "2009-11-17T18:45:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 37, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "sy{tGxc{kV???LABF?B??J" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:45:55.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 18.81, + "elevation" : "", + "lat" : 45.5313019, + "lon" : -122.6964448, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northwest Northrup Street", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:46:19.000+00:00", + "lat" : 45.53122, + "lon" : -122.69659, + "name" : "NW Northrup St. & NW 22nd Ave. (P2)", + "vertexType" : "NORMAL" + }, + "transitLeg" : false, + "walkingBike" : false + } + ], + "startTime" : "2009-11-17T18:19:04.000+00:00", + "tooSloped" : false, + "transfers" : 1, + "transitTime" : 511, + "waitingTime" : 128, + "walkDistance" : 2530.76, + "walkLimitExceeded" : false, + "walkTime" : 996 + } + ] +] \ No newline at end of file diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap index adc5ee69635..0f00b88e65b 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap @@ -230,10 +230,10 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 2261, + "duration" : 2020, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T18:38:41.000+00:00", + "endTime" : "2009-11-17T18:35:19.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -257,42 +257,62 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ] + }, + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] } ] }, - "generalizedCost" : 4281, + "generalizedCost" : 4023, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 920.33, - "endTime" : "2009-11-17T18:12:58.000+00:00", + "distance" : 1022.47, + "endTime" : "2009-11-17T18:14:50.000+00:00", "from" : { - "departure" : "2009-11-17T18:01:00.000+00:00", + "departure" : "2009-11-17T18:01:39.000+00:00", "lat" : 45.51726, "lon" : -122.64847, "name" : "SE Morrison St. & SE 17th Ave. (P1)", "vertexType" : "NORMAL" }, - "generalizedCost" : 1398, + "generalizedCost" : 1543, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 45, - "points" : "kaytG~wqkV?T?fCAl@?RmC?oCAmCAoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" + "length" : 37, + "points" : "kaytG~wqkV?T?fCAl@?R?jE?rC?t@?hEAvD?R?R?|@?dBAP?PAxD?nD?X?jE?bA?t@?N?Z?ZAX?^@bBAt@?zC?N?J?NQ?O?sA@?W" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:01:00.000+00:00", + "startTime" : "2009-11-17T18:01:39.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 87.68, + "distance" : 956.36, "elevation" : "", "lat" : 45.517186, "lon" : -122.6484704, @@ -305,50 +325,24 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "absoluteDirection" : "NORTH", "area" : false, "bogusName" : false, - "distance" : 641.04, - "elevation" : "", - "lat" : 45.5171903, - "lon" : -122.6495956, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Southeast 16th Avenue", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTH", - "area" : false, - "bogusName" : false, - "distance" : 168.89, - "elevation" : "", - "lat" : 45.5228912, - "lon" : -122.6495528, - "relativeDirection" : "CONTINUE", - "stayOn" : false, - "streetName" : "Northeast 16th Avenue", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTHEAST", - "area" : false, - "bogusName" : false, - "distance" : 22.74, + "distance" : 66.13, "elevation" : "", - "lat" : 45.524409, - "lon" : -122.6495675, + "lat" : 45.5172325, + "lon" : -122.6607432, "relativeDirection" : "RIGHT", "stayOn" : false, - "streetName" : "Northeast Sandy Boulevard", + "streetName" : "Southeast Grand Avenue", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:12:58.000+00:00", - "departure" : "2009-11-17T18:12:58.000+00:00", - "lat" : 45.524581, - "lon" : -122.649367, - "name" : "NE Sandy & 16th", - "stopCode" : "5060", - "stopId" : "prt:5060", + "arrival" : "2009-11-17T18:14:50.000+00:00", + "departure" : "2009-11-17T18:14:50.000+00:00", + "lat" : 45.517828, + "lon" : -122.660632, + "name" : "SE Grand & Alder", + "stopCode" : "11485", + "stopId" : "prt:11485", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -362,312 +356,403 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 3602.73, - "endTime" : "2009-11-17T18:25:49.000+00:00", + "distance" : 1580.92, + "endTime" : "2009-11-17T18:18:49.000+00:00", "from" : { - "arrival" : "2009-11-17T18:12:58.000+00:00", - "departure" : "2009-11-17T18:12:58.000+00:00", - "lat" : 45.524581, - "lon" : -122.649367, - "name" : "NE Sandy & 16th", - "stopCode" : "5060", - "stopId" : "prt:5060", - "stopIndex" : 92, - "stopSequence" : 93, + "arrival" : "2009-11-17T18:14:50.000+00:00", + "departure" : "2009-11-17T18:14:50.000+00:00", + "lat" : 45.517828, + "lon" : -122.660632, + "name" : "SE Grand & Alder", + "stopCode" : "11485", + "stopId" : "prt:11485", + "stopIndex" : 11, + "stopSequence" : 12, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1371, - "headsign" : "Beaverton TC", + "generalizedCost" : 839, + "headsign" : "Jantzen Beach", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:13:32.000+00:00", - "departure" : "2009-11-17T18:13:32.000+00:00", - "lat" : 45.523767, - "lon" : -122.651428, - "name" : "NE Sandy & 14th", - "stopCode" : "5058", - "stopId" : "prt:5058", - "stopIndex" : 93, - "stopSequence" : 94, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:14:00.000+00:00", - "departure" : "2009-11-17T18:14:00.000+00:00", - "lat" : 45.523103, - "lon" : -122.653064, - "name" : "NE Sandy & 12th", - "stopCode" : "5055", - "stopId" : "prt:5055", - "stopIndex" : 94, - "stopSequence" : 95, + "arrival" : "2009-11-17T18:15:25.000+00:00", + "departure" : "2009-11-17T18:15:25.000+00:00", + "lat" : 45.519986, + "lon" : -122.660636, + "name" : "SE Grand & Oak", + "stopCode" : "2174", + "stopId" : "prt:2174", + "stopIndex" : 12, + "stopSequence" : 13, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:14:47.000+00:00", - "departure" : "2009-11-17T18:14:47.000+00:00", - "lat" : 45.523024, - "lon" : -122.656526, - "name" : "E Burnside & NE 9th", - "stopCode" : "819", - "stopId" : "prt:819", - "stopIndex" : 95, - "stopSequence" : 96, + "arrival" : "2009-11-17T18:16:10.000+00:00", + "departure" : "2009-11-17T18:16:10.000+00:00", + "lat" : 45.522782, + "lon" : -122.660589, + "name" : "SE Grand & E Burnside", + "stopCode" : "2167", + "stopId" : "prt:2167", + "stopIndex" : 13, + "stopSequence" : 14, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:15:24.000+00:00", - "departure" : "2009-11-17T18:15:24.000+00:00", - "lat" : 45.523012, - "lon" : -122.659365, - "name" : "E Burnside & NE 6th", - "stopCode" : "805", - "stopId" : "prt:805", - "stopIndex" : 96, - "stopSequence" : 97, + "arrival" : "2009-11-17T18:16:39.000+00:00", + "departure" : "2009-11-17T18:16:39.000+00:00", + "lat" : 45.524582, + "lon" : -122.660578, + "name" : "NE Grand & Davis", + "stopCode" : "8829", + "stopId" : "prt:8829", + "stopIndex" : 14, + "stopSequence" : 15, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:15:52.000+00:00", - "departure" : "2009-11-17T18:15:52.000+00:00", - "lat" : 45.523015, - "lon" : -122.661534, - "name" : "E Burnside & NE M L King", - "stopCode" : "705", - "stopId" : "prt:705", - "stopIndex" : 97, - "stopSequence" : 98, + "arrival" : "2009-11-17T18:17:26.000+00:00", + "departure" : "2009-11-17T18:17:26.000+00:00", + "lat" : 45.527519, + "lon" : -122.66056, + "name" : "NE Grand & Hoyt", + "stopCode" : "2169", + "stopId" : "prt:2169", + "stopIndex" : 15, + "stopSequence" : 16, "vertexType" : "TRANSIT", "zoneId" : "1" }, { "arrival" : "2009-11-17T18:18:00.000+00:00", "departure" : "2009-11-17T18:18:00.000+00:00", - "lat" : 45.523249, - "lon" : -122.671269, - "name" : "W Burnside & Burnside Bridge", - "stopCode" : "689", - "stopId" : "prt:689", - "stopIndex" : 98, - "stopSequence" : 99, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:19:00.000+00:00", - "departure" : "2009-11-17T18:19:00.000+00:00", - "lat" : 45.523169, - "lon" : -122.675893, - "name" : "W Burnside & NW 5th", - "stopCode" : "782", - "stopId" : "prt:782", - "stopIndex" : 99, - "stopSequence" : 100, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:20:17.000+00:00", - "departure" : "2009-11-17T18:20:17.000+00:00", - "lat" : 45.523115, - "lon" : -122.678939, - "name" : "W Burnside & NW Park", - "stopCode" : "716", - "stopId" : "prt:716", - "stopIndex" : 100, - "stopSequence" : 101, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:21:25.000+00:00", - "departure" : "2009-11-17T18:21:25.000+00:00", - "lat" : 45.523048, - "lon" : -122.681606, - "name" : "W Burnside & NW 10th", - "stopCode" : "10791", - "stopId" : "prt:10791", - "stopIndex" : 101, - "stopSequence" : 102, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:22:14.000+00:00", - "departure" : "2009-11-17T18:22:14.000+00:00", - "lat" : 45.523, - "lon" : -122.683535, - "name" : "W Burnside & NW 12th", - "stopCode" : "11032", - "stopId" : "prt:11032", - "stopIndex" : 102, - "stopSequence" : 103, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:24:09.000+00:00", - "departure" : "2009-11-17T18:24:09.000+00:00", - "lat" : 45.522985, - "lon" : -122.688091, - "name" : "W Burnside & NW 17th", - "stopCode" : "10809", - "stopId" : "prt:10809", - "stopIndex" : 103, - "stopSequence" : 104, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:25:00.000+00:00", - "departure" : "2009-11-17T18:25:00.000+00:00", - "lat" : 45.523097, - "lon" : -122.690083, - "name" : "W Burnside & NW 19th", - "stopCode" : "735", - "stopId" : "prt:735", - "stopIndex" : 104, - "stopSequence" : 105, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:25:21.000+00:00", - "departure" : "2009-11-17T18:25:21.000+00:00", - "lat" : 45.523176, - "lon" : -122.692139, - "name" : "W Burnside & NW 20th", - "stopCode" : "741", - "stopId" : "prt:741", - "stopIndex" : 105, - "stopSequence" : 106, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:25:31.000+00:00", - "departure" : "2009-11-17T18:25:31.000+00:00", - "lat" : 45.52322, - "lon" : -122.69313, - "name" : "W Burnside & NW 20th Pl", - "stopCode" : "742", - "stopId" : "prt:742", - "stopIndex" : 106, - "stopSequence" : 107, + "lat" : 45.529602, + "lon" : -122.660529, + "name" : "NE Grand & Pacific", + "stopCode" : "2175", + "stopId" : "prt:2175", + "stopIndex" : 16, + "stopSequence" : 17, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 94, - "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB" + "length" : 35, + "points" : "meytGtdtkVC?OAQ?}B?mC?{BA??Q?oCAmC?mC?qBA??]?mC?mCAm@???aB?{AC[?kDAsCBq@A??uA?iCAgC?w@???yA?sCCoCAiB@" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Burnside/Stark", - "routeId" : "prt:20", - "routeLongName" : "Burnside/Stark", - "routeShortName" : "20", + "route" : "Martin Luther King Jr Blvd", + "routeId" : "prt:6", + "routeLongName" : "Martin Luther King Jr Blvd", + "routeShortName" : "6", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:12:58.000+00:00", + "startTime" : "2009-11-17T18:14:50.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T18:25:49.000+00:00", - "departure" : "2009-11-17T18:25:49.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 107, - "stopSequence" : 108, + "arrival" : "2009-11-17T18:18:49.000+00:00", + "departure" : "2009-11-17T18:18:49.000+00:00", + "lat" : 45.532047, + "lon" : -122.660537, + "name" : "NE Grand & Wasco", + "stopCode" : "10953", + "stopId" : "prt:10953", + "stopIndex" : 17, + "stopSequence" : 18, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "2002", - "tripId" : "prt:200W1200" + "tripBlockId" : "604", + "tripId" : "prt:60W1210" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 999.1, - "endTime" : "2009-11-17T18:38:41.000+00:00", + "distance" : 51.83, + "endTime" : "2009-11-17T18:19:32.000+00:00", "from" : { - "arrival" : "2009-11-17T18:25:49.000+00:00", - "departure" : "2009-11-17T18:25:49.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", + "arrival" : "2009-11-17T18:18:49.000+00:00", + "departure" : "2009-11-17T18:18:49.000+00:00", + "lat" : 45.532047, + "lon" : -122.660537, + "name" : "NE Grand & Wasco", + "stopCode" : "10953", + "stopId" : "prt:10953", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1511, + "generalizedCost" : 80, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 29, - "points" : "ugztGdzzkVL?ATClAI|DK?G?mCBkCDoCDmCBoCDkCBoCB[?sBD]?Y@eA@K?C?K?W@{A@M@C@I?_CB?G" + "length" : 10, + "points" : "g~{tGjctkV?B`@?\\?R?@?@?BC???I" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:25:49.000+00:00", + "startTime" : "2009-11-17T18:18:49.000+00:00", "steps" : [ { - "absoluteDirection" : "WEST", + "absoluteDirection" : "SOUTH", "area" : false, "bogusName" : false, - "distance" : 113.27, + "distance" : 51.83, "elevation" : "", - "lat" : 45.5232491, - "lon" : -122.6949067, + "lat" : 45.532047, + "lon" : -122.6605564, "relativeDirection" : "DEPART", "stayOn" : false, - "streetName" : "West Burnside Street", + "streetName" : "Northeast Grand Avenue", "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:19:32.000+00:00", + "departure" : "2009-11-17T18:22:05.000+00:00", + "lat" : 45.531586, + "lon" : -122.660482, + "name" : "NE Multnomah & Grand", + "stopCode" : "4043", + "stopId" : "prt:4043", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 3448.6, + "endTime" : "2009-11-17T18:34:55.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:19:32.000+00:00", + "departure" : "2009-11-17T18:22:05.000+00:00", + "lat" : 45.531586, + "lon" : -122.660482, + "name" : "NE Multnomah & Grand", + "stopCode" : "4043", + "stopId" : "prt:4043", + "stopIndex" : 82, + "stopSequence" : 83, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1523, + "headsign" : "Montgomery Park", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ + { + "arrival" : "2009-11-17T18:23:09.000+00:00", + "departure" : "2009-11-17T18:23:09.000+00:00", + "lat" : 45.531159, + "lon" : -122.66293, + "name" : "NE Multnomah & 3rd", + "stopCode" : "11492", + "stopId" : "prt:11492", + "stopIndex" : 83, + "stopSequence" : 84, + "vertexType" : "TRANSIT", + "zoneId" : "0" }, { - "absoluteDirection" : "NORTH", - "area" : false, - "bogusName" : false, - "distance" : 882.16, - "elevation" : "", - "lat" : 45.5233204, - "lon" : -122.696357, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Northwest 22nd Avenue", - "walkingBike" : false + "arrival" : "2009-11-17T18:25:00.000+00:00", + "departure" : "2009-11-17T18:25:00.000+00:00", + "lat" : 45.530005, + "lon" : -122.666476, + "name" : "Rose Quarter Transit Center", + "stopCode" : "2592", + "stopId" : "prt:2592", + "stopIndex" : 84, + "stopSequence" : 85, + "vertexType" : "TRANSIT", + "zoneId" : "0" }, { - "absoluteDirection" : "EAST", + "arrival" : "2009-11-17T18:28:20.000+00:00", + "departure" : "2009-11-17T18:28:20.000+00:00", + "lat" : 45.526655, + "lon" : -122.676462, + "name" : "NW Glisan & 6th", + "stopCode" : "10803", + "stopId" : "prt:10803", + "stopIndex" : 85, + "stopSequence" : 86, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:29:15.000+00:00", + "departure" : "2009-11-17T18:29:15.000+00:00", + "lat" : 45.528799, + "lon" : -122.677238, + "name" : "NW Station Way & Union Station", + "stopCode" : "12801", + "stopId" : "prt:12801", + "stopIndex" : 86, + "stopSequence" : 87, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:31:00.000+00:00", + "departure" : "2009-11-17T18:31:00.000+00:00", + "lat" : 45.531582, + "lon" : -122.681193, + "name" : "NW Northrup & 10th", + "stopCode" : "12802", + "stopId" : "prt:12802", + "stopIndex" : 87, + "stopSequence" : 88, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:31:33.000+00:00", + "departure" : "2009-11-17T18:31:33.000+00:00", + "lat" : 45.531534, + "lon" : -122.683319, + "name" : "NW 12th & Northrup", + "stopCode" : "12796", + "stopId" : "prt:12796", + "stopIndex" : 88, + "stopSequence" : 89, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:32:04.000+00:00", + "departure" : "2009-11-17T18:32:04.000+00:00", + "lat" : 45.531503, + "lon" : -122.685357, + "name" : "NW Northrup & 14th", + "stopCode" : "10775", + "stopId" : "prt:10775", + "stopIndex" : 89, + "stopSequence" : 90, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:33:07.000+00:00", + "departure" : "2009-11-17T18:33:07.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 90, + "stopSequence" : 91, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:34:24.000+00:00", + "departure" : "2009-11-17T18:34:24.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 91, + "stopSequence" : 92, + "vertexType" : "TRANSIT", + "zoneId" : "1" + } + ], + "legGeometry" : { + "length" : 101, + "points" : "}z{tG~btkV?^?nE?V@Z?PH\\Nb@`@~@Rf@??`@bANb@FV@R?P?pE?jA@h@AnAbBl@LFJN\\f@LT??NXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Broadway/Halsey", + "routeId" : "prt:77", + "routeLongName" : "Broadway/Halsey", + "routeShortName" : "77", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:22:05.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:34:55.000+00:00", + "departure" : "2009-11-17T18:34:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 92, + "stopSequence" : 93, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "7736", + "tripId" : "prt:771W1160" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 18.81, + "endTime" : "2009-11-17T18:35:19.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:34:55.000+00:00", + "departure" : "2009-11-17T18:34:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 37, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 7, + "points" : "sy{tGxc{kV???LABF?B??J" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:34:55.000+00:00", + "steps" : [ + { + "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 3.68, + "distance" : 18.81, "elevation" : "", - "lat" : 45.5312508, - "lon" : -122.6966386, - "relativeDirection" : "RIGHT", + "lat" : 45.5313019, + "lon" : -122.6964448, + "relativeDirection" : "DEPART", "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:38:41.000+00:00", + "arrival" : "2009-11-17T18:35:19.000+00:00", "lat" : 45.53122, "lon" : -122.69659, "name" : "NW Northrup St. & NW 22nd Ave. (P2)", @@ -677,21 +762,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:01:00.000+00:00", + "startTime" : "2009-11-17T18:01:39.000+00:00", "tooSloped" : false, - "transfers" : 0, - "transitTime" : 771, - "waitingTime" : 0, - "walkDistance" : 1919.43, + "transfers" : 1, + "transitTime" : 1009, + "waitingTime" : 153, + "walkDistance" : 1093.11, "walkLimitExceeded" : false, - "walkTime" : 1490 + "walkTime" : 858 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1470, + "duration" : 2261, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T18:39:22.000+00:00", + "endTime" : "2009-11-17T18:38:41.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -718,39 +803,39 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ] }, - "generalizedCost" : 2315, + "generalizedCost" : 4281, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 62.01, - "endTime" : "2009-11-17T18:15:40.000+00:00", + "distance" : 920.33, + "endTime" : "2009-11-17T18:12:58.000+00:00", "from" : { - "departure" : "2009-11-17T18:14:52.000+00:00", + "departure" : "2009-11-17T18:01:00.000+00:00", "lat" : 45.51726, "lon" : -122.64847, "name" : "SE Morrison St. & SE 17th Ave. (P1)", "vertexType" : "NORMAL" }, - "generalizedCost" : 95, + "generalizedCost" : 1398, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 4, - "points" : "kaytG~wqkV?T?fCG?" + "length" : 45, + "points" : "kaytG~wqkV?T?fCAl@?RmC?oCAmCAoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:14:52.000+00:00", + "startTime" : "2009-11-17T18:01:00.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 62.02, + "distance" : 87.68, "elevation" : "", "lat" : 45.517186, "lon" : -122.6484704, @@ -758,16 +843,55 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "stayOn" : false, "streetName" : "Southeast Morrison Street", "walkingBike" : false - } - ], - "to" : { - "arrival" : "2009-11-17T18:15:40.000+00:00", - "departure" : "2009-11-17T18:15:40.000+00:00", - "lat" : 45.517226, - "lon" : -122.649266, - "name" : "SE Morrison & 16th", - "stopCode" : "4019", - "stopId" : "prt:4019", + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 641.04, + "elevation" : "", + "lat" : 45.5171903, + "lon" : -122.6495956, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 168.89, + "elevation" : "", + "lat" : 45.5228912, + "lon" : -122.6495528, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 22.74, + "elevation" : "", + "lat" : 45.524409, + "lon" : -122.6495675, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northeast Sandy Boulevard", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -781,416 +905,312 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 5218.86, - "endTime" : "2009-11-17T18:35:54.000+00:00", + "distance" : 3602.73, + "endTime" : "2009-11-17T18:25:49.000+00:00", "from" : { - "arrival" : "2009-11-17T18:15:40.000+00:00", - "departure" : "2009-11-17T18:15:40.000+00:00", - "lat" : 45.517226, - "lon" : -122.649266, - "name" : "SE Morrison & 16th", - "stopCode" : "4019", - "stopId" : "prt:4019", - "stopIndex" : 50, - "stopSequence" : 51, + "arrival" : "2009-11-17T18:12:58.000+00:00", + "departure" : "2009-11-17T18:12:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "stopIndex" : 92, + "stopSequence" : 93, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1814, - "headsign" : "Montgomery Park", + "generalizedCost" : 1371, + "headsign" : "Beaverton TC", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:16:15.000+00:00", - "departure" : "2009-11-17T18:16:15.000+00:00", - "lat" : 45.517253, - "lon" : -122.651354, - "name" : "SE Morrison & 14th", - "stopCode" : "4016", - "stopId" : "prt:4016", - "stopIndex" : 51, - "stopSequence" : 52, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:17:00.000+00:00", - "departure" : "2009-11-17T18:17:00.000+00:00", - "lat" : 45.517299, - "lon" : -122.654067, - "name" : "SE Morrison & 12th", - "stopCode" : "4014", - "stopId" : "prt:4014", - "stopIndex" : 52, - "stopSequence" : 53, + "arrival" : "2009-11-17T18:13:32.000+00:00", + "departure" : "2009-11-17T18:13:32.000+00:00", + "lat" : 45.523767, + "lon" : -122.651428, + "name" : "NE Sandy & 14th", + "stopCode" : "5058", + "stopId" : "prt:5058", + "stopIndex" : 93, + "stopSequence" : 94, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:17:38.000+00:00", - "departure" : "2009-11-17T18:17:38.000+00:00", - "lat" : 45.517292, - "lon" : -122.656563, - "name" : "SE Morrison & 9th", - "stopCode" : "4026", - "stopId" : "prt:4026", - "stopIndex" : 53, - "stopSequence" : 54, + "arrival" : "2009-11-17T18:14:00.000+00:00", + "departure" : "2009-11-17T18:14:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:18:08.000+00:00", - "departure" : "2009-11-17T18:18:08.000+00:00", - "lat" : 45.517322, - "lon" : -122.65847, - "name" : "SE Morrison & 7th", - "stopCode" : "4025", - "stopId" : "prt:4025", - "stopIndex" : 54, - "stopSequence" : 55, + "arrival" : "2009-11-17T18:14:47.000+00:00", + "departure" : "2009-11-17T18:14:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:18:39.000+00:00", - "departure" : "2009-11-17T18:18:39.000+00:00", - "lat" : 45.517298, - "lon" : -122.660523, - "name" : "SE Morrison & Grand", - "stopCode" : "4013", - "stopId" : "prt:4013", - "stopIndex" : 55, - "stopSequence" : 56, + "arrival" : "2009-11-17T18:15:24.000+00:00", + "departure" : "2009-11-17T18:15:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:20:03.000+00:00", - "departure" : "2009-11-17T18:20:03.000+00:00", - "lat" : 45.517351, - "lon" : -122.66601, - "name" : "Morrison Bridge", - "stopCode" : "4029", - "stopId" : "prt:4029", - "stopIndex" : 56, - "stopSequence" : 57, + "arrival" : "2009-11-17T18:15:52.000+00:00", + "departure" : "2009-11-17T18:15:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:22:27.000+00:00", - "departure" : "2009-11-17T18:22:27.000+00:00", - "lat" : 45.51959, - "lon" : -122.674599, - "name" : "SW Washington & 3rd", - "stopCode" : "6158", - "stopId" : "prt:6158", - "stopIndex" : 57, - "stopSequence" : 58, + "arrival" : "2009-11-17T18:18:00.000+00:00", + "departure" : "2009-11-17T18:18:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:23:00.000+00:00", - "departure" : "2009-11-17T18:23:00.000+00:00", - "lat" : 45.520129, - "lon" : -122.676635, - "name" : "SW Washington & 5th", - "stopCode" : "6160", - "stopId" : "prt:6160", - "stopIndex" : 58, - "stopSequence" : 59, + "arrival" : "2009-11-17T18:19:00.000+00:00", + "departure" : "2009-11-17T18:19:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:23:52.000+00:00", - "departure" : "2009-11-17T18:23:52.000+00:00", - "lat" : 45.520695, - "lon" : -122.678657, - "name" : "SW Washington & Broadway", - "stopCode" : "6137", - "stopId" : "prt:6137", - "stopIndex" : 59, - "stopSequence" : 60, + "arrival" : "2009-11-17T18:20:17.000+00:00", + "departure" : "2009-11-17T18:20:17.000+00:00", + "lat" : 45.523115, + "lon" : -122.678939, + "name" : "W Burnside & NW Park", + "stopCode" : "716", + "stopId" : "prt:716", + "stopIndex" : 100, + "stopSequence" : 101, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:24:34.000+00:00", - "departure" : "2009-11-17T18:24:34.000+00:00", - "lat" : 45.521124, - "lon" : -122.6803, - "name" : "SW Washington & 9th", - "stopCode" : "6169", - "stopId" : "prt:6169", - "stopIndex" : 60, - "stopSequence" : 61, + "arrival" : "2009-11-17T18:21:25.000+00:00", + "departure" : "2009-11-17T18:21:25.000+00:00", + "lat" : 45.523048, + "lon" : -122.681606, + "name" : "W Burnside & NW 10th", + "stopCode" : "10791", + "stopId" : "prt:10791", + "stopIndex" : 101, + "stopSequence" : 102, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:25:47.000+00:00", - "departure" : "2009-11-17T18:25:47.000+00:00", - "lat" : 45.521094, - "lon" : -122.682819, - "name" : "SW 11th & Alder", - "stopCode" : "9600", - "stopId" : "prt:9600", - "stopIndex" : 61, - "stopSequence" : 62, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:26:36.000+00:00", - "departure" : "2009-11-17T18:26:36.000+00:00", - "lat" : 45.52055, - "lon" : -122.683933, - "name" : "SW Morrison & 12th", - "stopCode" : "9598", - "stopId" : "prt:9598", - "stopIndex" : 62, - "stopSequence" : 63, + "arrival" : "2009-11-17T18:22:14.000+00:00", + "departure" : "2009-11-17T18:22:14.000+00:00", + "lat" : 45.523, + "lon" : -122.683535, + "name" : "W Burnside & NW 12th", + "stopCode" : "11032", + "stopId" : "prt:11032", + "stopIndex" : 102, + "stopSequence" : 103, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:27:25.000+00:00", - "departure" : "2009-11-17T18:27:25.000+00:00", - "lat" : 45.521063, - "lon" : -122.685848, - "name" : "SW Morrison & 14th", - "stopCode" : "9708", - "stopId" : "prt:9708", - "stopIndex" : 63, - "stopSequence" : 64, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:28:18.000+00:00", - "departure" : "2009-11-17T18:28:18.000+00:00", - "lat" : 45.521641, - "lon" : -122.687932, - "name" : "SW Morrison & 16th", - "stopCode" : "9613", - "stopId" : "prt:9613", - "stopIndex" : 64, - "stopSequence" : 65, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:29:00.000+00:00", - "departure" : "2009-11-17T18:29:00.000+00:00", - "lat" : 45.52206, - "lon" : -122.689577, - "name" : "SW Morrison & 17th", - "stopCode" : "9599", - "stopId" : "prt:9599", - "stopIndex" : 65, - "stopSequence" : 66, + "arrival" : "2009-11-17T18:24:09.000+00:00", + "departure" : "2009-11-17T18:24:09.000+00:00", + "lat" : 45.522985, + "lon" : -122.688091, + "name" : "W Burnside & NW 17th", + "stopCode" : "10809", + "stopId" : "prt:10809", + "stopIndex" : 103, + "stopSequence" : 104, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:29:54.000+00:00", - "departure" : "2009-11-17T18:29:54.000+00:00", + "arrival" : "2009-11-17T18:25:00.000+00:00", + "departure" : "2009-11-17T18:25:00.000+00:00", "lat" : 45.523097, "lon" : -122.690083, "name" : "W Burnside & NW 19th", "stopCode" : "735", "stopId" : "prt:735", - "stopIndex" : 66, - "stopSequence" : 67, + "stopIndex" : 104, + "stopSequence" : 105, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:30:31.000+00:00", - "departure" : "2009-11-17T18:30:31.000+00:00", + "arrival" : "2009-11-17T18:25:21.000+00:00", + "departure" : "2009-11-17T18:25:21.000+00:00", "lat" : 45.523176, "lon" : -122.692139, "name" : "W Burnside & NW 20th", "stopCode" : "741", "stopId" : "prt:741", - "stopIndex" : 67, - "stopSequence" : 68, + "stopIndex" : 105, + "stopSequence" : 106, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:30:48.000+00:00", - "departure" : "2009-11-17T18:30:48.000+00:00", + "arrival" : "2009-11-17T18:25:31.000+00:00", + "departure" : "2009-11-17T18:25:31.000+00:00", "lat" : 45.52322, "lon" : -122.69313, "name" : "W Burnside & NW 20th Pl", "stopCode" : "742", "stopId" : "prt:742", - "stopIndex" : 68, - "stopSequence" : 69, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:31:20.000+00:00", - "departure" : "2009-11-17T18:31:20.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 69, - "stopSequence" : 70, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:32:18.000+00:00", - "departure" : "2009-11-17T18:32:18.000+00:00", - "lat" : 45.523512, - "lon" : -122.698081, - "name" : "W Burnside & NW 23rd", - "stopCode" : "755", - "stopId" : "prt:755", - "stopIndex" : 70, - "stopSequence" : 71, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:33:11.000+00:00", - "departure" : "2009-11-17T18:33:11.000+00:00", - "lat" : 45.525416, - "lon" : -122.698381, - "name" : "NW 23rd & Flanders", - "stopCode" : "7157", - "stopId" : "prt:7157", - "stopIndex" : 71, - "stopSequence" : 72, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:34:05.000+00:00", - "departure" : "2009-11-17T18:34:05.000+00:00", - "lat" : 45.527543, - "lon" : -122.698473, - "name" : "NW 23rd & Irving", - "stopCode" : "7161", - "stopId" : "prt:7161", - "stopIndex" : 72, - "stopSequence" : 73, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:35:00.000+00:00", - "departure" : "2009-11-17T18:35:00.000+00:00", - "lat" : 45.529681, - "lon" : -122.698529, - "name" : "NW 23rd & Lovejoy", - "stopCode" : "7163", - "stopId" : "prt:7163", - "stopIndex" : 73, - "stopSequence" : 74, + "stopIndex" : 106, + "stopSequence" : 107, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 135, - "points" : "kaytG||qkVA~@?jE?tC???r@AhE?jE?rA???tBAjE?nD???X?hE?xC??Ah@?pE?~C???J?`@?vAAvBEbE?jEAlE?`BAbB@d@??@tAAj@Cx@Cb@Cp@_@dEcAtFoA`IS~@i@`BmAzDi@zAc@pAi@~C??Id@u@jEm@bD??If@u@jEk@bD??If@u@|DW`B??CPs@|Du@lElBz@??VJbCfAk@dD??Id@w@rEWvAId@AF??Q~@s@`Ei@~C??Ib@u@dEWzA??]jB]MQSe@WOKOKIIQe@GWE]GnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APkAh@o@?sCB{BD??S?mCDmCDyBB??U?mCDmCDyBB??S?oCDmCDmCBo@@" + "length" : 94, + "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Belmont/NW 23rd", - "routeId" : "prt:15", - "routeLongName" : "Belmont/NW 23rd", - "routeShortName" : "15", + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:15:40.000+00:00", + "startTime" : "2009-11-17T18:12:58.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T18:35:54.000+00:00", - "departure" : "2009-11-17T18:35:54.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", - "stopIndex" : 74, - "stopSequence" : 75, + "arrival" : "2009-11-17T18:25:49.000+00:00", + "departure" : "2009-11-17T18:25:49.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 107, + "stopSequence" : 108, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "1549", - "tripId" : "prt:150W1400" + "tripBlockId" : "2002", + "tripId" : "prt:200W1200" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 266.21, - "endTime" : "2009-11-17T18:39:22.000+00:00", + "distance" : 999.1, + "endTime" : "2009-11-17T18:38:41.000+00:00", "from" : { - "arrival" : "2009-11-17T18:35:54.000+00:00", - "departure" : "2009-11-17T18:35:54.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", + "arrival" : "2009-11-17T18:25:49.000+00:00", + "departure" : "2009-11-17T18:25:49.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 405, + "generalizedCost" : 1511, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 13, - "points" : "}~{tGnq{kV?LVAF?J?L?rBCLA?Q?EAyAEcH?G" + "length" : 29, + "points" : "ugztGdzzkVL?ATClAI|DK?G?mCBkCDoCDmCBoCDkCBoCB[?sBD]?Y@eA@K?C?K?W@{A@M@C@I?_CB?G" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:35:54.000+00:00", + "startTime" : "2009-11-17T18:25:49.000+00:00", "steps" : [ { - "absoluteDirection" : "SOUTH", + "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 104.46, + "distance" : 113.27, "elevation" : "", - "lat" : 45.5321578, - "lon" : -122.6987026, + "lat" : 45.5232491, + "lon" : -122.6949067, "relativeDirection" : "DEPART", "stayOn" : false, - "streetName" : "Northwest 23rd Avenue", + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 882.16, + "elevation" : "", + "lat" : 45.5233204, + "lon" : -122.696357, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 22nd Avenue", "walkingBike" : false }, { "absoluteDirection" : "EAST", "area" : false, "bogusName" : false, - "distance" : 161.77, + "distance" : 3.68, "elevation" : "", - "lat" : 45.5312188, - "lon" : -122.6986675, - "relativeDirection" : "LEFT", + "lat" : 45.5312508, + "lon" : -122.6966386, + "relativeDirection" : "RIGHT", "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:39:22.000+00:00", + "arrival" : "2009-11-17T18:38:41.000+00:00", "lat" : 45.53122, "lon" : -122.69659, "name" : "NW Northrup St. & NW 22nd Ave. (P2)", @@ -1200,21 +1220,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:14:52.000+00:00", + "startTime" : "2009-11-17T18:01:00.000+00:00", "tooSloped" : false, "transfers" : 0, - "transitTime" : 1214, + "transitTime" : 771, "waitingTime" : 0, - "walkDistance" : 328.22, + "walkDistance" : 1919.43, "walkLimitExceeded" : false, - "walkTime" : 256 + "walkTime" : 1490 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 2275, + "duration" : 1470, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T18:53:55.000+00:00", + "endTime" : "2009-11-17T18:39:22.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -1241,39 +1261,39 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ] }, - "generalizedCost" : 4295, + "generalizedCost" : 2315, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 920.33, - "endTime" : "2009-11-17T18:27:58.000+00:00", + "distance" : 62.01, + "endTime" : "2009-11-17T18:15:40.000+00:00", "from" : { - "departure" : "2009-11-17T18:16:00.000+00:00", + "departure" : "2009-11-17T18:14:52.000+00:00", "lat" : 45.51726, "lon" : -122.64847, "name" : "SE Morrison St. & SE 17th Ave. (P1)", "vertexType" : "NORMAL" }, - "generalizedCost" : 1398, + "generalizedCost" : 95, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 45, - "points" : "kaytG~wqkV?T?fCAl@?RmC?oCAmCAoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" + "length" : 4, + "points" : "kaytG~wqkV?T?fCG?" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:16:00.000+00:00", + "startTime" : "2009-11-17T18:14:52.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 87.68, + "distance" : 62.02, "elevation" : "", "lat" : 45.517186, "lon" : -122.6484704, @@ -1281,55 +1301,16 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "stayOn" : false, "streetName" : "Southeast Morrison Street", "walkingBike" : false - }, - { - "absoluteDirection" : "NORTH", - "area" : false, - "bogusName" : false, - "distance" : 641.04, - "elevation" : "", - "lat" : 45.5171903, - "lon" : -122.6495956, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Southeast 16th Avenue", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTH", - "area" : false, - "bogusName" : false, - "distance" : 168.89, - "elevation" : "", - "lat" : 45.5228912, - "lon" : -122.6495528, - "relativeDirection" : "CONTINUE", - "stayOn" : false, - "streetName" : "Northeast 16th Avenue", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTHEAST", - "area" : false, - "bogusName" : false, - "distance" : 22.74, - "elevation" : "", - "lat" : 45.524409, - "lon" : -122.6495675, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Northeast Sandy Boulevard", - "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:27:58.000+00:00", - "departure" : "2009-11-17T18:27:58.000+00:00", - "lat" : 45.524581, - "lon" : -122.649367, - "name" : "NE Sandy & 16th", - "stopCode" : "5060", - "stopId" : "prt:5060", + "arrival" : "2009-11-17T18:15:40.000+00:00", + "departure" : "2009-11-17T18:15:40.000+00:00", + "lat" : 45.517226, + "lon" : -122.649266, + "name" : "SE Morrison & 16th", + "stopCode" : "4019", + "stopId" : "prt:4019", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -1343,312 +1324,416 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 3602.73, - "endTime" : "2009-11-17T18:41:03.000+00:00", + "distance" : 5218.86, + "endTime" : "2009-11-17T18:35:54.000+00:00", "from" : { - "arrival" : "2009-11-17T18:27:58.000+00:00", - "departure" : "2009-11-17T18:27:58.000+00:00", - "lat" : 45.524581, - "lon" : -122.649367, - "name" : "NE Sandy & 16th", - "stopCode" : "5060", - "stopId" : "prt:5060", - "stopIndex" : 92, - "stopSequence" : 93, + "arrival" : "2009-11-17T18:15:40.000+00:00", + "departure" : "2009-11-17T18:15:40.000+00:00", + "lat" : 45.517226, + "lon" : -122.649266, + "name" : "SE Morrison & 16th", + "stopCode" : "4019", + "stopId" : "prt:4019", + "stopIndex" : 50, + "stopSequence" : 51, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1385, - "headsign" : "23rd Ave to Tichner", + "generalizedCost" : 1814, + "headsign" : "Montgomery Park", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:28:32.000+00:00", - "departure" : "2009-11-17T18:28:32.000+00:00", - "lat" : 45.523767, - "lon" : -122.651428, - "name" : "NE Sandy & 14th", - "stopCode" : "5058", - "stopId" : "prt:5058", - "stopIndex" : 93, - "stopSequence" : 94, + "arrival" : "2009-11-17T18:16:15.000+00:00", + "departure" : "2009-11-17T18:16:15.000+00:00", + "lat" : 45.517253, + "lon" : -122.651354, + "name" : "SE Morrison & 14th", + "stopCode" : "4016", + "stopId" : "prt:4016", + "stopIndex" : 51, + "stopSequence" : 52, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:29:00.000+00:00", - "departure" : "2009-11-17T18:29:00.000+00:00", - "lat" : 45.523103, - "lon" : -122.653064, - "name" : "NE Sandy & 12th", - "stopCode" : "5055", - "stopId" : "prt:5055", - "stopIndex" : 94, - "stopSequence" : 95, + "arrival" : "2009-11-17T18:17:00.000+00:00", + "departure" : "2009-11-17T18:17:00.000+00:00", + "lat" : 45.517299, + "lon" : -122.654067, + "name" : "SE Morrison & 12th", + "stopCode" : "4014", + "stopId" : "prt:4014", + "stopIndex" : 52, + "stopSequence" : 53, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:29:47.000+00:00", - "departure" : "2009-11-17T18:29:47.000+00:00", - "lat" : 45.523024, - "lon" : -122.656526, - "name" : "E Burnside & NE 9th", - "stopCode" : "819", - "stopId" : "prt:819", - "stopIndex" : 95, - "stopSequence" : 96, + "arrival" : "2009-11-17T18:17:38.000+00:00", + "departure" : "2009-11-17T18:17:38.000+00:00", + "lat" : 45.517292, + "lon" : -122.656563, + "name" : "SE Morrison & 9th", + "stopCode" : "4026", + "stopId" : "prt:4026", + "stopIndex" : 53, + "stopSequence" : 54, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:30:24.000+00:00", - "departure" : "2009-11-17T18:30:24.000+00:00", - "lat" : 45.523012, - "lon" : -122.659365, - "name" : "E Burnside & NE 6th", - "stopCode" : "805", - "stopId" : "prt:805", - "stopIndex" : 96, - "stopSequence" : 97, + "arrival" : "2009-11-17T18:18:08.000+00:00", + "departure" : "2009-11-17T18:18:08.000+00:00", + "lat" : 45.517322, + "lon" : -122.65847, + "name" : "SE Morrison & 7th", + "stopCode" : "4025", + "stopId" : "prt:4025", + "stopIndex" : 54, + "stopSequence" : 55, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:30:52.000+00:00", - "departure" : "2009-11-17T18:30:52.000+00:00", - "lat" : 45.523015, - "lon" : -122.661534, - "name" : "E Burnside & NE M L King", - "stopCode" : "705", - "stopId" : "prt:705", - "stopIndex" : 97, - "stopSequence" : 98, + "arrival" : "2009-11-17T18:18:39.000+00:00", + "departure" : "2009-11-17T18:18:39.000+00:00", + "lat" : 45.517298, + "lon" : -122.660523, + "name" : "SE Morrison & Grand", + "stopCode" : "4013", + "stopId" : "prt:4013", + "stopIndex" : 55, + "stopSequence" : 56, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:33:00.000+00:00", - "departure" : "2009-11-17T18:33:00.000+00:00", - "lat" : 45.523249, - "lon" : -122.671269, - "name" : "W Burnside & Burnside Bridge", - "stopCode" : "689", - "stopId" : "prt:689", - "stopIndex" : 98, - "stopSequence" : 99, + "arrival" : "2009-11-17T18:20:03.000+00:00", + "departure" : "2009-11-17T18:20:03.000+00:00", + "lat" : 45.517351, + "lon" : -122.66601, + "name" : "Morrison Bridge", + "stopCode" : "4029", + "stopId" : "prt:4029", + "stopIndex" : 56, + "stopSequence" : 57, "vertexType" : "TRANSIT", - "zoneId" : "0" + "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:34:00.000+00:00", - "departure" : "2009-11-17T18:34:00.000+00:00", - "lat" : 45.523169, - "lon" : -122.675893, - "name" : "W Burnside & NW 5th", - "stopCode" : "782", - "stopId" : "prt:782", - "stopIndex" : 99, - "stopSequence" : 100, + "arrival" : "2009-11-17T18:22:27.000+00:00", + "departure" : "2009-11-17T18:22:27.000+00:00", + "lat" : 45.51959, + "lon" : -122.674599, + "name" : "SW Washington & 3rd", + "stopCode" : "6158", + "stopId" : "prt:6158", + "stopIndex" : 57, + "stopSequence" : 58, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:35:17.000+00:00", - "departure" : "2009-11-17T18:35:17.000+00:00", - "lat" : 45.523115, - "lon" : -122.678939, - "name" : "W Burnside & NW Park", - "stopCode" : "716", - "stopId" : "prt:716", - "stopIndex" : 100, - "stopSequence" : 101, + "arrival" : "2009-11-17T18:23:00.000+00:00", + "departure" : "2009-11-17T18:23:00.000+00:00", + "lat" : 45.520129, + "lon" : -122.676635, + "name" : "SW Washington & 5th", + "stopCode" : "6160", + "stopId" : "prt:6160", + "stopIndex" : 58, + "stopSequence" : 59, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:36:25.000+00:00", - "departure" : "2009-11-17T18:36:25.000+00:00", - "lat" : 45.523048, - "lon" : -122.681606, - "name" : "W Burnside & NW 10th", - "stopCode" : "10791", - "stopId" : "prt:10791", - "stopIndex" : 101, - "stopSequence" : 102, + "arrival" : "2009-11-17T18:23:52.000+00:00", + "departure" : "2009-11-17T18:23:52.000+00:00", + "lat" : 45.520695, + "lon" : -122.678657, + "name" : "SW Washington & Broadway", + "stopCode" : "6137", + "stopId" : "prt:6137", + "stopIndex" : 59, + "stopSequence" : 60, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:37:14.000+00:00", - "departure" : "2009-11-17T18:37:14.000+00:00", - "lat" : 45.523, - "lon" : -122.683535, - "name" : "W Burnside & NW 12th", - "stopCode" : "11032", - "stopId" : "prt:11032", - "stopIndex" : 102, - "stopSequence" : 103, + "arrival" : "2009-11-17T18:24:34.000+00:00", + "departure" : "2009-11-17T18:24:34.000+00:00", + "lat" : 45.521124, + "lon" : -122.6803, + "name" : "SW Washington & 9th", + "stopCode" : "6169", + "stopId" : "prt:6169", + "stopIndex" : 60, + "stopSequence" : 61, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:39:09.000+00:00", - "departure" : "2009-11-17T18:39:09.000+00:00", - "lat" : 45.522985, - "lon" : -122.688091, - "name" : "W Burnside & NW 17th", - "stopCode" : "10809", - "stopId" : "prt:10809", - "stopIndex" : 103, - "stopSequence" : 104, + "arrival" : "2009-11-17T18:25:47.000+00:00", + "departure" : "2009-11-17T18:25:47.000+00:00", + "lat" : 45.521094, + "lon" : -122.682819, + "name" : "SW 11th & Alder", + "stopCode" : "9600", + "stopId" : "prt:9600", + "stopIndex" : 61, + "stopSequence" : 62, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:26:36.000+00:00", + "departure" : "2009-11-17T18:26:36.000+00:00", + "lat" : 45.52055, + "lon" : -122.683933, + "name" : "SW Morrison & 12th", + "stopCode" : "9598", + "stopId" : "prt:9598", + "stopIndex" : 62, + "stopSequence" : 63, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:27:25.000+00:00", + "departure" : "2009-11-17T18:27:25.000+00:00", + "lat" : 45.521063, + "lon" : -122.685848, + "name" : "SW Morrison & 14th", + "stopCode" : "9708", + "stopId" : "prt:9708", + "stopIndex" : 63, + "stopSequence" : 64, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:40:00.000+00:00", - "departure" : "2009-11-17T18:40:00.000+00:00", + "arrival" : "2009-11-17T18:28:18.000+00:00", + "departure" : "2009-11-17T18:28:18.000+00:00", + "lat" : 45.521641, + "lon" : -122.687932, + "name" : "SW Morrison & 16th", + "stopCode" : "9613", + "stopId" : "prt:9613", + "stopIndex" : 64, + "stopSequence" : 65, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:00.000+00:00", + "departure" : "2009-11-17T18:29:00.000+00:00", + "lat" : 45.52206, + "lon" : -122.689577, + "name" : "SW Morrison & 17th", + "stopCode" : "9599", + "stopId" : "prt:9599", + "stopIndex" : 65, + "stopSequence" : 66, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:29:54.000+00:00", + "departure" : "2009-11-17T18:29:54.000+00:00", "lat" : 45.523097, "lon" : -122.690083, "name" : "W Burnside & NW 19th", "stopCode" : "735", "stopId" : "prt:735", - "stopIndex" : 104, - "stopSequence" : 105, + "stopIndex" : 66, + "stopSequence" : 67, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:40:27.000+00:00", - "departure" : "2009-11-17T18:40:27.000+00:00", + "arrival" : "2009-11-17T18:30:31.000+00:00", + "departure" : "2009-11-17T18:30:31.000+00:00", "lat" : 45.523176, "lon" : -122.692139, "name" : "W Burnside & NW 20th", "stopCode" : "741", "stopId" : "prt:741", - "stopIndex" : 105, - "stopSequence" : 106, + "stopIndex" : 67, + "stopSequence" : 68, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:40:40.000+00:00", - "departure" : "2009-11-17T18:40:40.000+00:00", + "arrival" : "2009-11-17T18:30:48.000+00:00", + "departure" : "2009-11-17T18:30:48.000+00:00", "lat" : 45.52322, "lon" : -122.69313, "name" : "W Burnside & NW 20th Pl", "stopCode" : "742", "stopId" : "prt:742", - "stopIndex" : 106, - "stopSequence" : 107, + "stopIndex" : 68, + "stopSequence" : 69, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:31:20.000+00:00", + "departure" : "2009-11-17T18:31:20.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 69, + "stopSequence" : 70, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:32:18.000+00:00", + "departure" : "2009-11-17T18:32:18.000+00:00", + "lat" : 45.523512, + "lon" : -122.698081, + "name" : "W Burnside & NW 23rd", + "stopCode" : "755", + "stopId" : "prt:755", + "stopIndex" : 70, + "stopSequence" : 71, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:33:11.000+00:00", + "departure" : "2009-11-17T18:33:11.000+00:00", + "lat" : 45.525416, + "lon" : -122.698381, + "name" : "NW 23rd & Flanders", + "stopCode" : "7157", + "stopId" : "prt:7157", + "stopIndex" : 71, + "stopSequence" : 72, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:34:05.000+00:00", + "departure" : "2009-11-17T18:34:05.000+00:00", + "lat" : 45.527543, + "lon" : -122.698473, + "name" : "NW 23rd & Irving", + "stopCode" : "7161", + "stopId" : "prt:7161", + "stopIndex" : 72, + "stopSequence" : 73, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:35:00.000+00:00", + "departure" : "2009-11-17T18:35:00.000+00:00", + "lat" : 45.529681, + "lon" : -122.698529, + "name" : "NW 23rd & Lovejoy", + "stopCode" : "7163", + "stopId" : "prt:7163", + "stopIndex" : 73, + "stopSequence" : 74, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 94, - "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB" + "length" : 135, + "points" : "kaytG||qkVA~@?jE?tC???r@AhE?jE?rA???tBAjE?nD???X?hE?xC??Ah@?pE?~C???J?`@?vAAvBEbE?jEAlE?`BAbB@d@??@tAAj@Cx@Cb@Cp@_@dEcAtFoA`IS~@i@`BmAzDi@zAc@pAi@~C??Id@u@jEm@bD??If@u@jEk@bD??If@u@|DW`B??CPs@|Du@lElBz@??VJbCfAk@dD??Id@w@rEWvAId@AF??Q~@s@`Ei@~C??Ib@u@dEWzA??]jB]MQSe@WOKOKIIQe@GWE]GnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APkAh@o@?sCB{BD??S?mCDmCDyBB??U?mCDmCDyBB??S?oCDmCDmCBo@@" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Burnside/Stark", - "routeId" : "prt:20", - "routeLongName" : "Burnside/Stark", - "routeShortName" : "20", + "route" : "Belmont/NW 23rd", + "routeId" : "prt:15", + "routeLongName" : "Belmont/NW 23rd", + "routeShortName" : "15", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:27:58.000+00:00", + "startTime" : "2009-11-17T18:15:40.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T18:41:03.000+00:00", - "departure" : "2009-11-17T18:41:03.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 107, - "stopSequence" : 108, + "arrival" : "2009-11-17T18:35:54.000+00:00", + "departure" : "2009-11-17T18:35:54.000+00:00", + "lat" : 45.532159, + "lon" : -122.698634, + "name" : "NW 23rd & Overton", + "stopCode" : "8981", + "stopId" : "prt:8981", + "stopIndex" : 74, + "stopSequence" : 75, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "2071", - "tripId" : "prt:200W1210" + "tripBlockId" : "1549", + "tripId" : "prt:150W1400" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 999.1, - "endTime" : "2009-11-17T18:53:55.000+00:00", + "distance" : 266.21, + "endTime" : "2009-11-17T18:39:22.000+00:00", "from" : { - "arrival" : "2009-11-17T18:41:03.000+00:00", - "departure" : "2009-11-17T18:41:03.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", + "arrival" : "2009-11-17T18:35:54.000+00:00", + "departure" : "2009-11-17T18:35:54.000+00:00", + "lat" : 45.532159, + "lon" : -122.698634, + "name" : "NW 23rd & Overton", + "stopCode" : "8981", + "stopId" : "prt:8981", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1511, + "generalizedCost" : 405, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 29, - "points" : "ugztGdzzkVL?ATClAI|DK?G?mCBkCDoCDmCBoCDkCBoCB[?sBD]?Y@eA@K?C?K?W@{A@M@C@I?_CB?G" + "length" : 13, + "points" : "}~{tGnq{kV?LVAF?J?L?rBCLA?Q?EAyAEcH?G" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:41:03.000+00:00", + "startTime" : "2009-11-17T18:35:54.000+00:00", "steps" : [ { - "absoluteDirection" : "WEST", + "absoluteDirection" : "SOUTH", "area" : false, "bogusName" : false, - "distance" : 113.27, + "distance" : 104.46, "elevation" : "", - "lat" : 45.5232491, - "lon" : -122.6949067, + "lat" : 45.5321578, + "lon" : -122.6987026, "relativeDirection" : "DEPART", "stayOn" : false, - "streetName" : "West Burnside Street", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTH", - "area" : false, - "bogusName" : false, - "distance" : 882.16, - "elevation" : "", - "lat" : 45.5233204, - "lon" : -122.696357, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Northwest 22nd Avenue", + "streetName" : "Northwest 23rd Avenue", "walkingBike" : false }, { "absoluteDirection" : "EAST", "area" : false, "bogusName" : false, - "distance" : 3.68, + "distance" : 161.77, "elevation" : "", - "lat" : 45.5312508, - "lon" : -122.6966386, - "relativeDirection" : "RIGHT", + "lat" : 45.5312188, + "lon" : -122.6986675, + "relativeDirection" : "LEFT", "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:53:55.000+00:00", + "arrival" : "2009-11-17T18:39:22.000+00:00", "lat" : 45.53122, "lon" : -122.69659, "name" : "NW Northrup St. & NW 22nd Ave. (P2)", @@ -1658,21 +1743,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:16:00.000+00:00", + "startTime" : "2009-11-17T18:14:52.000+00:00", "tooSloped" : false, "transfers" : 0, - "transitTime" : 785, + "transitTime" : 1214, "waitingTime" : 0, - "walkDistance" : 1919.43, + "walkDistance" : 328.22, "walkLimitExceeded" : false, - "walkTime" : 1490 + "walkTime" : 256 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1540, + "duration" : 2020, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T18:55:32.000+00:00", + "endTime" : "2009-11-17T18:51:19.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -1696,42 +1781,62 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ] + }, + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] } ] }, - "generalizedCost" : 2385, + "generalizedCost" : 4023, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 62.01, - "endTime" : "2009-11-17T18:30:40.000+00:00", + "distance" : 1022.47, + "endTime" : "2009-11-17T18:30:50.000+00:00", "from" : { - "departure" : "2009-11-17T18:29:52.000+00:00", + "departure" : "2009-11-17T18:17:39.000+00:00", "lat" : 45.51726, "lon" : -122.64847, "name" : "SE Morrison St. & SE 17th Ave. (P1)", "vertexType" : "NORMAL" }, - "generalizedCost" : 95, + "generalizedCost" : 1543, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 4, - "points" : "kaytG~wqkV?T?fCG?" + "length" : 37, + "points" : "kaytG~wqkV?T?fCAl@?R?jE?rC?t@?hEAvD?R?R?|@?dBAP?PAxD?nD?X?jE?bA?t@?N?Z?ZAX?^@bBAt@?zC?N?J?NQ?O?sA@?W" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:29:52.000+00:00", + "startTime" : "2009-11-17T18:17:39.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 62.02, + "distance" : 956.36, "elevation" : "", "lat" : 45.517186, "lon" : -122.6484704, @@ -1739,16 +1844,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "stayOn" : false, "streetName" : "Southeast Morrison Street", "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 66.13, + "elevation" : "", + "lat" : 45.5172325, + "lon" : -122.6607432, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast Grand Avenue", + "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:30:40.000+00:00", - "departure" : "2009-11-17T18:30:40.000+00:00", - "lat" : 45.517226, - "lon" : -122.649266, - "name" : "SE Morrison & 16th", - "stopCode" : "4019", - "stopId" : "prt:4019", + "arrival" : "2009-11-17T18:30:50.000+00:00", + "departure" : "2009-11-17T18:30:50.000+00:00", + "lat" : 45.517828, + "lon" : -122.660632, + "name" : "SE Grand & Alder", + "stopCode" : "11485", + "stopId" : "prt:11485", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -1762,416 +1880,403 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 5218.86, - "endTime" : "2009-11-17T18:52:04.000+00:00", + "distance" : 1580.92, + "endTime" : "2009-11-17T18:34:53.000+00:00", "from" : { - "arrival" : "2009-11-17T18:30:40.000+00:00", - "departure" : "2009-11-17T18:30:40.000+00:00", - "lat" : 45.517226, - "lon" : -122.649266, - "name" : "SE Morrison & 16th", - "stopCode" : "4019", - "stopId" : "prt:4019", - "stopIndex" : 50, - "stopSequence" : 51, + "arrival" : "2009-11-17T18:30:50.000+00:00", + "departure" : "2009-11-17T18:30:50.000+00:00", + "lat" : 45.517828, + "lon" : -122.660632, + "name" : "SE Grand & Alder", + "stopCode" : "11485", + "stopId" : "prt:11485", + "stopIndex" : 11, + "stopSequence" : 12, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1884, - "headsign" : "NW 27th & Thurman", + "generalizedCost" : 843, + "headsign" : "Jantzen Beach", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:31:15.000+00:00", - "departure" : "2009-11-17T18:31:15.000+00:00", - "lat" : 45.517253, - "lon" : -122.651354, - "name" : "SE Morrison & 14th", - "stopCode" : "4016", - "stopId" : "prt:4016", - "stopIndex" : 51, - "stopSequence" : 52, + "arrival" : "2009-11-17T18:31:25.000+00:00", + "departure" : "2009-11-17T18:31:25.000+00:00", + "lat" : 45.519986, + "lon" : -122.660636, + "name" : "SE Grand & Oak", + "stopCode" : "2174", + "stopId" : "prt:2174", + "stopIndex" : 12, + "stopSequence" : 13, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:32:00.000+00:00", - "departure" : "2009-11-17T18:32:00.000+00:00", - "lat" : 45.517299, - "lon" : -122.654067, - "name" : "SE Morrison & 12th", - "stopCode" : "4014", - "stopId" : "prt:4014", - "stopIndex" : 52, - "stopSequence" : 53, + "arrival" : "2009-11-17T18:32:10.000+00:00", + "departure" : "2009-11-17T18:32:10.000+00:00", + "lat" : 45.522782, + "lon" : -122.660589, + "name" : "SE Grand & E Burnside", + "stopCode" : "2167", + "stopId" : "prt:2167", + "stopIndex" : 13, + "stopSequence" : 14, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:32:38.000+00:00", - "departure" : "2009-11-17T18:32:38.000+00:00", - "lat" : 45.517292, - "lon" : -122.656563, - "name" : "SE Morrison & 9th", - "stopCode" : "4026", - "stopId" : "prt:4026", - "stopIndex" : 53, - "stopSequence" : 54, + "arrival" : "2009-11-17T18:32:39.000+00:00", + "departure" : "2009-11-17T18:32:39.000+00:00", + "lat" : 45.524582, + "lon" : -122.660578, + "name" : "NE Grand & Davis", + "stopCode" : "8829", + "stopId" : "prt:8829", + "stopIndex" : 14, + "stopSequence" : 15, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:33:08.000+00:00", - "departure" : "2009-11-17T18:33:08.000+00:00", - "lat" : 45.517322, - "lon" : -122.65847, - "name" : "SE Morrison & 7th", - "stopCode" : "4025", - "stopId" : "prt:4025", - "stopIndex" : 54, - "stopSequence" : 55, + "arrival" : "2009-11-17T18:33:26.000+00:00", + "departure" : "2009-11-17T18:33:26.000+00:00", + "lat" : 45.527519, + "lon" : -122.66056, + "name" : "NE Grand & Hoyt", + "stopCode" : "2169", + "stopId" : "prt:2169", + "stopIndex" : 15, + "stopSequence" : 16, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:33:39.000+00:00", - "departure" : "2009-11-17T18:33:39.000+00:00", - "lat" : 45.517298, - "lon" : -122.660523, - "name" : "SE Morrison & Grand", - "stopCode" : "4013", - "stopId" : "prt:4013", - "stopIndex" : 55, - "stopSequence" : 56, + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.529602, + "lon" : -122.660529, + "name" : "NE Grand & Pacific", + "stopCode" : "2175", + "stopId" : "prt:2175", + "stopIndex" : 16, + "stopSequence" : 17, "vertexType" : "TRANSIT", "zoneId" : "1" - }, + } + ], + "legGeometry" : { + "length" : 35, + "points" : "meytGtdtkVC?OAQ?}B?mC?{BA??Q?oCAmC?mC?qBA??]?mC?mCAm@???aB?{AC[?kDAsCBq@A??uA?iCAgC?w@???yA?sCCoCAiB@" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Martin Luther King Jr Blvd", + "routeId" : "prt:6", + "routeLongName" : "Martin Luther King Jr Blvd", + "routeShortName" : "6", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:30:50.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:34:53.000+00:00", + "departure" : "2009-11-17T18:34:53.000+00:00", + "lat" : 45.532047, + "lon" : -122.660537, + "name" : "NE Grand & Wasco", + "stopCode" : "10953", + "stopId" : "prt:10953", + "stopIndex" : 17, + "stopSequence" : 18, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "transitLeg" : true, + "tripBlockId" : "605", + "tripId" : "prt:60W1220" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 51.83, + "endTime" : "2009-11-17T18:35:36.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:34:53.000+00:00", + "departure" : "2009-11-17T18:34:53.000+00:00", + "lat" : 45.532047, + "lon" : -122.660537, + "name" : "NE Grand & Wasco", + "stopCode" : "10953", + "stopId" : "prt:10953", + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + "generalizedCost" : 80, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 10, + "points" : "g~{tGjctkV?B`@?\\?R?@?@?BC???I" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:34:53.000+00:00", + "steps" : [ { - "arrival" : "2009-11-17T18:35:03.000+00:00", - "departure" : "2009-11-17T18:35:03.000+00:00", - "lat" : 45.517351, - "lon" : -122.66601, - "name" : "Morrison Bridge", - "stopCode" : "4029", - "stopId" : "prt:4029", - "stopIndex" : 56, - "stopSequence" : 57, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 51.83, + "elevation" : "", + "lat" : 45.532047, + "lon" : -122.6605564, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Northeast Grand Avenue", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:35:36.000+00:00", + "departure" : "2009-11-17T18:38:05.000+00:00", + "lat" : 45.531586, + "lon" : -122.660482, + "name" : "NE Multnomah & Grand", + "stopCode" : "4043", + "stopId" : "prt:4043", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 3448.6, + "endTime" : "2009-11-17T18:50:55.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:35:36.000+00:00", + "departure" : "2009-11-17T18:38:05.000+00:00", + "lat" : 45.531586, + "lon" : -122.660482, + "name" : "NE Multnomah & Grand", + "stopCode" : "4043", + "stopId" : "prt:4043", + "stopIndex" : 82, + "stopSequence" : 83, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1519, + "headsign" : "Montgomery Park", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ { - "arrival" : "2009-11-17T18:37:27.000+00:00", - "departure" : "2009-11-17T18:37:27.000+00:00", - "lat" : 45.51959, - "lon" : -122.674599, - "name" : "SW Washington & 3rd", - "stopCode" : "6158", - "stopId" : "prt:6158", - "stopIndex" : 57, - "stopSequence" : 58, + "arrival" : "2009-11-17T18:39:09.000+00:00", + "departure" : "2009-11-17T18:39:09.000+00:00", + "lat" : 45.531159, + "lon" : -122.66293, + "name" : "NE Multnomah & 3rd", + "stopCode" : "11492", + "stopId" : "prt:11492", + "stopIndex" : 83, + "stopSequence" : 84, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:38:00.000+00:00", - "departure" : "2009-11-17T18:38:00.000+00:00", - "lat" : 45.520129, - "lon" : -122.676635, - "name" : "SW Washington & 5th", - "stopCode" : "6160", - "stopId" : "prt:6160", - "stopIndex" : 58, - "stopSequence" : 59, + "arrival" : "2009-11-17T18:41:00.000+00:00", + "departure" : "2009-11-17T18:41:00.000+00:00", + "lat" : 45.530005, + "lon" : -122.666476, + "name" : "Rose Quarter Transit Center", + "stopCode" : "2592", + "stopId" : "prt:2592", + "stopIndex" : 84, + "stopSequence" : 85, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:38:52.000+00:00", - "departure" : "2009-11-17T18:38:52.000+00:00", - "lat" : 45.520695, - "lon" : -122.678657, - "name" : "SW Washington & Broadway", - "stopCode" : "6137", - "stopId" : "prt:6137", - "stopIndex" : 59, - "stopSequence" : 60, + "arrival" : "2009-11-17T18:44:20.000+00:00", + "departure" : "2009-11-17T18:44:20.000+00:00", + "lat" : 45.526655, + "lon" : -122.676462, + "name" : "NW Glisan & 6th", + "stopCode" : "10803", + "stopId" : "prt:10803", + "stopIndex" : 85, + "stopSequence" : 86, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:39:34.000+00:00", - "departure" : "2009-11-17T18:39:34.000+00:00", - "lat" : 45.521124, - "lon" : -122.6803, - "name" : "SW Washington & 9th", - "stopCode" : "6169", - "stopId" : "prt:6169", - "stopIndex" : 60, - "stopSequence" : 61, + "arrival" : "2009-11-17T18:45:15.000+00:00", + "departure" : "2009-11-17T18:45:15.000+00:00", + "lat" : 45.528799, + "lon" : -122.677238, + "name" : "NW Station Way & Union Station", + "stopCode" : "12801", + "stopId" : "prt:12801", + "stopIndex" : 86, + "stopSequence" : 87, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:40:47.000+00:00", - "departure" : "2009-11-17T18:40:47.000+00:00", - "lat" : 45.521094, - "lon" : -122.682819, - "name" : "SW 11th & Alder", - "stopCode" : "9600", - "stopId" : "prt:9600", - "stopIndex" : 61, - "stopSequence" : 62, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:41:36.000+00:00", - "departure" : "2009-11-17T18:41:36.000+00:00", - "lat" : 45.52055, - "lon" : -122.683933, - "name" : "SW Morrison & 12th", - "stopCode" : "9598", - "stopId" : "prt:9598", - "stopIndex" : 62, - "stopSequence" : 63, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:42:25.000+00:00", - "departure" : "2009-11-17T18:42:25.000+00:00", - "lat" : 45.521063, - "lon" : -122.685848, - "name" : "SW Morrison & 14th", - "stopCode" : "9708", - "stopId" : "prt:9708", - "stopIndex" : 63, - "stopSequence" : 64, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:43:18.000+00:00", - "departure" : "2009-11-17T18:43:18.000+00:00", - "lat" : 45.521641, - "lon" : -122.687932, - "name" : "SW Morrison & 16th", - "stopCode" : "9613", - "stopId" : "prt:9613", - "stopIndex" : 64, - "stopSequence" : 65, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:44:00.000+00:00", - "departure" : "2009-11-17T18:44:00.000+00:00", - "lat" : 45.52206, - "lon" : -122.689577, - "name" : "SW Morrison & 17th", - "stopCode" : "9599", - "stopId" : "prt:9599", - "stopIndex" : 65, - "stopSequence" : 66, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:45:03.000+00:00", - "departure" : "2009-11-17T18:45:03.000+00:00", - "lat" : 45.523097, - "lon" : -122.690083, - "name" : "W Burnside & NW 19th", - "stopCode" : "735", - "stopId" : "prt:735", - "stopIndex" : 66, - "stopSequence" : 67, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:45:46.000+00:00", - "departure" : "2009-11-17T18:45:46.000+00:00", - "lat" : 45.523176, - "lon" : -122.692139, - "name" : "W Burnside & NW 20th", - "stopCode" : "741", - "stopId" : "prt:741", - "stopIndex" : 67, - "stopSequence" : 68, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:46:07.000+00:00", - "departure" : "2009-11-17T18:46:07.000+00:00", - "lat" : 45.52322, - "lon" : -122.69313, - "name" : "W Burnside & NW 20th Pl", - "stopCode" : "742", - "stopId" : "prt:742", - "stopIndex" : 68, - "stopSequence" : 69, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T18:46:44.000+00:00", - "departure" : "2009-11-17T18:46:44.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 69, - "stopSequence" : 70, + "arrival" : "2009-11-17T18:47:00.000+00:00", + "departure" : "2009-11-17T18:47:00.000+00:00", + "lat" : 45.531582, + "lon" : -122.681193, + "name" : "NW Northrup & 10th", + "stopCode" : "12802", + "stopId" : "prt:12802", + "stopIndex" : 87, + "stopSequence" : 88, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:47:51.000+00:00", - "departure" : "2009-11-17T18:47:51.000+00:00", - "lat" : 45.523512, - "lon" : -122.698081, - "name" : "W Burnside & NW 23rd", - "stopCode" : "755", - "stopId" : "prt:755", - "stopIndex" : 70, - "stopSequence" : 71, + "arrival" : "2009-11-17T18:47:33.000+00:00", + "departure" : "2009-11-17T18:47:33.000+00:00", + "lat" : 45.531534, + "lon" : -122.683319, + "name" : "NW 12th & Northrup", + "stopCode" : "12796", + "stopId" : "prt:12796", + "stopIndex" : 88, + "stopSequence" : 89, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:48:53.000+00:00", - "departure" : "2009-11-17T18:48:53.000+00:00", - "lat" : 45.525416, - "lon" : -122.698381, - "name" : "NW 23rd & Flanders", - "stopCode" : "7157", - "stopId" : "prt:7157", - "stopIndex" : 71, - "stopSequence" : 72, + "arrival" : "2009-11-17T18:48:04.000+00:00", + "departure" : "2009-11-17T18:48:04.000+00:00", + "lat" : 45.531503, + "lon" : -122.685357, + "name" : "NW Northrup & 14th", + "stopCode" : "10775", + "stopId" : "prt:10775", + "stopIndex" : 89, + "stopSequence" : 90, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:49:56.000+00:00", - "departure" : "2009-11-17T18:49:56.000+00:00", - "lat" : 45.527543, - "lon" : -122.698473, - "name" : "NW 23rd & Irving", - "stopCode" : "7161", - "stopId" : "prt:7161", - "stopIndex" : 72, - "stopSequence" : 73, + "arrival" : "2009-11-17T18:49:07.000+00:00", + "departure" : "2009-11-17T18:49:07.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 90, + "stopSequence" : 91, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:51:00.000+00:00", - "departure" : "2009-11-17T18:51:00.000+00:00", - "lat" : 45.529681, - "lon" : -122.698529, - "name" : "NW 23rd & Lovejoy", - "stopCode" : "7163", - "stopId" : "prt:7163", - "stopIndex" : 73, - "stopSequence" : 74, + "arrival" : "2009-11-17T18:50:24.000+00:00", + "departure" : "2009-11-17T18:50:24.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 91, + "stopSequence" : 92, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 135, - "points" : "kaytG||qkVA~@?jE?tC???r@AhE?jE?rA???tBAjE?nD???X?hE?xC??Ah@?pE?~C???J?`@?vAAvBEbE?jEAlE?`BAbB@d@??@tAAj@Cx@Cb@Cp@_@dEcAtFoA`IS~@i@`BmAzDi@zAc@pAi@~C??Id@u@jEm@bD??If@u@jEk@bD??If@u@|DW`B??CPs@|Du@lElBz@??VJbCfAk@dD??Id@w@rEWvAId@AF??Q~@s@`Ei@~C??Ib@u@dEWzA??]jB]MQSe@WOKOKIIQe@GWE]GnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APkAh@o@?sCB{BD??S?mCDmCDyBB??U?mCDmCDyBB??S?oCDmCDmCBo@@" + "length" : 101, + "points" : "}z{tG~btkV?^?nE?V@Z?PH\\Nb@`@~@Rf@??`@bANb@FV@R?P?pE?jA@h@AnAbBl@LFJN\\f@LT??NXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Belmont/NW 23rd", - "routeId" : "prt:15", - "routeLongName" : "Belmont/NW 23rd", - "routeShortName" : "15", + "route" : "Broadway/Halsey", + "routeId" : "prt:77", + "routeLongName" : "Broadway/Halsey", + "routeShortName" : "77", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:30:40.000+00:00", + "startTime" : "2009-11-17T18:38:05.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T18:52:04.000+00:00", - "departure" : "2009-11-17T18:52:04.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", - "stopIndex" : 74, - "stopSequence" : 75, + "arrival" : "2009-11-17T18:50:55.000+00:00", + "departure" : "2009-11-17T18:50:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 92, + "stopSequence" : 93, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "1541", - "tripId" : "prt:150W1410" + "tripBlockId" : "7705", + "tripId" : "prt:771W1170" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 266.21, - "endTime" : "2009-11-17T18:55:32.000+00:00", + "distance" : 18.81, + "endTime" : "2009-11-17T18:51:19.000+00:00", "from" : { - "arrival" : "2009-11-17T18:52:04.000+00:00", - "departure" : "2009-11-17T18:52:04.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", + "arrival" : "2009-11-17T18:50:55.000+00:00", + "departure" : "2009-11-17T18:50:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 405, + "generalizedCost" : 37, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 13, - "points" : "}~{tGnq{kV?LVAF?J?L?rBCLA?Q?EAyAEcH?G" + "length" : 7, + "points" : "sy{tGxc{kV???LABF?B??J" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:52:04.000+00:00", + "startTime" : "2009-11-17T18:50:55.000+00:00", "steps" : [ { - "absoluteDirection" : "SOUTH", + "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 104.46, + "distance" : 18.81, "elevation" : "", - "lat" : 45.5321578, - "lon" : -122.6987026, + "lat" : 45.5313019, + "lon" : -122.6964448, "relativeDirection" : "DEPART", "stayOn" : false, - "streetName" : "Northwest 23rd Avenue", - "walkingBike" : false - }, - { - "absoluteDirection" : "EAST", - "area" : false, - "bogusName" : false, - "distance" : 161.77, - "elevation" : "", - "lat" : 45.5312188, - "lon" : -122.6986675, - "relativeDirection" : "LEFT", - "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:55:32.000+00:00", + "arrival" : "2009-11-17T18:51:19.000+00:00", "lat" : 45.53122, "lon" : -122.69659, "name" : "NW Northrup St. & NW 22nd Ave. (P2)", @@ -2181,21 +2286,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:29:52.000+00:00", + "startTime" : "2009-11-17T18:17:39.000+00:00", "tooSloped" : false, - "transfers" : 0, - "transitTime" : 1284, - "waitingTime" : 0, - "walkDistance" : 328.22, + "transfers" : 1, + "transitTime" : 1013, + "waitingTime" : 149, + "walkDistance" : 1093.11, "walkLimitExceeded" : false, - "walkTime" : 256 + "walkTime" : 858 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1849, + "duration" : 2275, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T19:08:19.000+00:00", + "endTime" : "2009-11-17T18:53:55.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -2219,62 +2324,42 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ] - }, - { - "legIndices" : [ - 2 - ], - "products" : [ - { - "amount" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "id" : "prt:8", - "name" : "regular" - } - ] } ] }, - "generalizedCost" : 3375, + "generalizedCost" : 4295, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 419.62, - "endTime" : "2009-11-17T18:42:54.000+00:00", + "distance" : 920.33, + "endTime" : "2009-11-17T18:27:58.000+00:00", "from" : { - "departure" : "2009-11-17T18:37:30.000+00:00", + "departure" : "2009-11-17T18:16:00.000+00:00", "lat" : 45.51726, "lon" : -122.64847, "name" : "SE Morrison St. & SE 17th Ave. (P1)", "vertexType" : "NORMAL" }, - "generalizedCost" : 636, + "generalizedCost" : 1398, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 14, - "points" : "kaytG~wqkV?T?fCAl@?R?jE?rC?t@?hEAvD?RN?L??O" + "length" : 45, + "points" : "kaytG~wqkV?T?fCAl@?RmC?oCAmCAoC?_CAM?aC??A?A?A?A??AA?AAA??AAA???A?A?A???A@A??@A@?@??A@?@?BcC?mCAmCAmC?QBIYIWOH" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:37:30.000+00:00", + "startTime" : "2009-11-17T18:16:00.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 403.66, + "distance" : 87.68, "elevation" : "", "lat" : 45.517186, "lon" : -122.6484704, @@ -2284,27 +2369,53 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false }, { - "absoluteDirection" : "SOUTH", + "absoluteDirection" : "NORTH", "area" : false, "bogusName" : false, - "distance" : 15.96, + "distance" : 641.04, "elevation" : "", - "lat" : 45.5172031, - "lon" : -122.6536511, - "relativeDirection" : "LEFT", + "lat" : 45.5171903, + "lon" : -122.6495956, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 168.89, + "elevation" : "", + "lat" : 45.5228912, + "lon" : -122.6495528, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "Northeast 16th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", + "area" : false, + "bogusName" : false, + "distance" : 22.74, + "elevation" : "", + "lat" : 45.524409, + "lon" : -122.6495675, + "relativeDirection" : "RIGHT", "stayOn" : false, - "streetName" : "Southeast 12th Avenue", + "streetName" : "Northeast Sandy Boulevard", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:42:54.000+00:00", - "departure" : "2009-11-17T18:42:54.000+00:00", - "lat" : 45.517059, - "lon" : -122.65358, - "name" : "SE 12th & Morrison", - "stopCode" : "6588", - "stopId" : "prt:6588", + "arrival" : "2009-11-17T18:27:58.000+00:00", + "departure" : "2009-11-17T18:27:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -2318,384 +2429,312 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 2035.62, - "endTime" : "2009-11-17T18:51:01.000+00:00", + "distance" : 3602.73, + "endTime" : "2009-11-17T18:41:03.000+00:00", "from" : { - "arrival" : "2009-11-17T18:42:54.000+00:00", - "departure" : "2009-11-17T18:42:54.000+00:00", - "lat" : 45.517059, - "lon" : -122.65358, - "name" : "SE 12th & Morrison", - "stopCode" : "6588", - "stopId" : "prt:6588", - "stopIndex" : 31, - "stopSequence" : 32, + "arrival" : "2009-11-17T18:27:58.000+00:00", + "departure" : "2009-11-17T18:27:58.000+00:00", + "lat" : 45.524581, + "lon" : -122.649367, + "name" : "NE Sandy & 16th", + "stopCode" : "5060", + "stopId" : "prt:5060", + "stopIndex" : 92, + "stopSequence" : 93, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1087, - "headsign" : "Rose Qtr TC", + "generalizedCost" : 1385, + "headsign" : "23rd Ave to Tichner", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:44:00.000+00:00", - "departure" : "2009-11-17T18:44:00.000+00:00", - "lat" : 45.519229, - "lon" : -122.653546, - "name" : "SE 12th & Stark", - "stopCode" : "6594", - "stopId" : "prt:6594", - "stopIndex" : 32, - "stopSequence" : 33, + "arrival" : "2009-11-17T18:28:32.000+00:00", + "departure" : "2009-11-17T18:28:32.000+00:00", + "lat" : 45.523767, + "lon" : -122.651428, + "name" : "NE Sandy & 14th", + "stopCode" : "5058", + "stopId" : "prt:5058", + "stopIndex" : 93, + "stopSequence" : 94, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:44:44.000+00:00", - "departure" : "2009-11-17T18:44:44.000+00:00", - "lat" : 45.520674, - "lon" : -122.653544, - "name" : "SE 12th & Pine", - "stopCode" : "6589", - "stopId" : "prt:6589", - "stopIndex" : 33, - "stopSequence" : 34, + "arrival" : "2009-11-17T18:29:00.000+00:00", + "departure" : "2009-11-17T18:29:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:46:00.000+00:00", - "departure" : "2009-11-17T18:46:00.000+00:00", - "lat" : 45.52318, - "lon" : -122.653507, - "name" : "NE 12th & Sandy", - "stopCode" : "6592", - "stopId" : "prt:6592", - "stopIndex" : 34, - "stopSequence" : 35, + "arrival" : "2009-11-17T18:29:47.000+00:00", + "departure" : "2009-11-17T18:29:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:47:45.000+00:00", - "departure" : "2009-11-17T18:47:45.000+00:00", - "lat" : 45.527449, - "lon" : -122.653462, - "name" : "NE 12th & Irving", - "stopCode" : "6582", - "stopId" : "prt:6582", - "stopIndex" : 35, - "stopSequence" : 36, + "arrival" : "2009-11-17T18:30:24.000+00:00", + "departure" : "2009-11-17T18:30:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:49:00.000+00:00", - "departure" : "2009-11-17T18:49:00.000+00:00", - "lat" : 45.529793, - "lon" : -122.654429, - "name" : "NE 11th & Holladay", - "stopCode" : "8513", - "stopId" : "prt:8513", - "stopIndex" : 36, - "stopSequence" : 37, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T18:49:39.000+00:00", - "departure" : "2009-11-17T18:49:39.000+00:00", - "lat" : 45.53135, - "lon" : -122.654497, - "name" : "NE 11th & Multnomah", - "stopCode" : "8938", - "stopId" : "prt:8938", - "stopIndex" : 37, - "stopSequence" : 38, + "arrival" : "2009-11-17T18:30:52.000+00:00", + "departure" : "2009-11-17T18:30:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, "vertexType" : "TRANSIT", - "zoneId" : "0" + "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:50:15.000+00:00", - "departure" : "2009-11-17T18:50:15.000+00:00", - "lat" : 45.531573, - "lon" : -122.656408, - "name" : "NE Multnomah & 9th", - "stopCode" : "4056", - "stopId" : "prt:4056", - "stopIndex" : 38, - "stopSequence" : 39, - "vertexType" : "TRANSIT", - "zoneId" : "0" - } - ], - "legGeometry" : { - "length" : 49, - "points" : "s`ytGhxrkV[?mCAmC?wBA??W?mC?{BA??Q?oC?mC?kBAa@?w@???wA?mCAmC?oCA}C?sDC??aBAm@@k@AY?uABU@I@IBQFb@fC}@d@OFO@q@???Q?]?gGA??[??nJ???b@?vK?rA" - }, - "mode" : "BUS", - "pathway" : false, - "realTime" : false, - "route" : "12th Ave", - "routeId" : "prt:70", - "routeLongName" : "12th Ave", - "routeShortName" : "70", - "routeType" : 3, - "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:42:54.000+00:00", - "steps" : [ ], - "to" : { - "arrival" : "2009-11-17T18:51:01.000+00:00", - "departure" : "2009-11-17T18:54:29.000+00:00", - "lat" : 45.531569, - "lon" : -122.659045, - "name" : "NE Multnomah & 7th", - "stopCode" : "4054", - "stopId" : "prt:4054", - "stopIndex" : 39, - "stopSequence" : 40, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - "transitLeg" : true, - "tripBlockId" : "7002", - "tripId" : "prt:700W1170" - }, - { - "agencyId" : "prt:prt", - "agencyName" : "TriMet", - "agencyTimeZoneOffset" : -28800000, - "agencyUrl" : "http://trimet.org", - "arrivalDelay" : 0, - "departureDelay" : 0, - "distance" : 3560.24, - "endTime" : "2009-11-17T19:07:55.000+00:00", - "from" : { - "arrival" : "2009-11-17T18:51:01.000+00:00", - "departure" : "2009-11-17T18:54:29.000+00:00", - "lat" : 45.531569, - "lon" : -122.659045, - "name" : "NE Multnomah & 7th", - "stopCode" : "4054", - "stopId" : "prt:4054", - "stopIndex" : 81, - "stopSequence" : 82, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - "generalizedCost" : 1614, - "headsign" : "Montgomery Park", - "interlineWithPreviousLeg" : false, - "intermediateStops" : [ - { - "arrival" : "2009-11-17T18:55:05.000+00:00", - "departure" : "2009-11-17T18:55:05.000+00:00", - "lat" : 45.531586, - "lon" : -122.660482, - "name" : "NE Multnomah & Grand", - "stopCode" : "4043", - "stopId" : "prt:4043", - "stopIndex" : 82, - "stopSequence" : 83, + "arrival" : "2009-11-17T18:33:00.000+00:00", + "departure" : "2009-11-17T18:33:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:56:09.000+00:00", - "departure" : "2009-11-17T18:56:09.000+00:00", - "lat" : 45.531159, - "lon" : -122.66293, - "name" : "NE Multnomah & 3rd", - "stopCode" : "11492", - "stopId" : "prt:11492", - "stopIndex" : 83, - "stopSequence" : 84, + "arrival" : "2009-11-17T18:34:00.000+00:00", + "departure" : "2009-11-17T18:34:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:58:00.000+00:00", - "departure" : "2009-11-17T18:58:00.000+00:00", - "lat" : 45.530005, - "lon" : -122.666476, - "name" : "Rose Quarter Transit Center", - "stopCode" : "2592", - "stopId" : "prt:2592", - "stopIndex" : 84, - "stopSequence" : 85, + "arrival" : "2009-11-17T18:35:17.000+00:00", + "departure" : "2009-11-17T18:35:17.000+00:00", + "lat" : 45.523115, + "lon" : -122.678939, + "name" : "W Burnside & NW Park", + "stopCode" : "716", + "stopId" : "prt:716", + "stopIndex" : 100, + "stopSequence" : 101, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:01:20.000+00:00", - "departure" : "2009-11-17T19:01:20.000+00:00", - "lat" : 45.526655, - "lon" : -122.676462, - "name" : "NW Glisan & 6th", - "stopCode" : "10803", - "stopId" : "prt:10803", - "stopIndex" : 85, - "stopSequence" : 86, + "arrival" : "2009-11-17T18:36:25.000+00:00", + "departure" : "2009-11-17T18:36:25.000+00:00", + "lat" : 45.523048, + "lon" : -122.681606, + "name" : "W Burnside & NW 10th", + "stopCode" : "10791", + "stopId" : "prt:10791", + "stopIndex" : 101, + "stopSequence" : 102, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:02:15.000+00:00", - "departure" : "2009-11-17T19:02:15.000+00:00", - "lat" : 45.528799, - "lon" : -122.677238, - "name" : "NW Station Way & Union Station", - "stopCode" : "12801", - "stopId" : "prt:12801", - "stopIndex" : 86, - "stopSequence" : 87, + "arrival" : "2009-11-17T18:37:14.000+00:00", + "departure" : "2009-11-17T18:37:14.000+00:00", + "lat" : 45.523, + "lon" : -122.683535, + "name" : "W Burnside & NW 12th", + "stopCode" : "11032", + "stopId" : "prt:11032", + "stopIndex" : 102, + "stopSequence" : 103, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:04:00.000+00:00", - "departure" : "2009-11-17T19:04:00.000+00:00", - "lat" : 45.531582, - "lon" : -122.681193, - "name" : "NW Northrup & 10th", - "stopCode" : "12802", - "stopId" : "prt:12802", - "stopIndex" : 87, - "stopSequence" : 88, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:04:33.000+00:00", - "departure" : "2009-11-17T19:04:33.000+00:00", - "lat" : 45.531534, - "lon" : -122.683319, - "name" : "NW 12th & Northrup", - "stopCode" : "12796", - "stopId" : "prt:12796", - "stopIndex" : 88, - "stopSequence" : 89, + "arrival" : "2009-11-17T18:39:09.000+00:00", + "departure" : "2009-11-17T18:39:09.000+00:00", + "lat" : 45.522985, + "lon" : -122.688091, + "name" : "W Burnside & NW 17th", + "stopCode" : "10809", + "stopId" : "prt:10809", + "stopIndex" : 103, + "stopSequence" : 104, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:05:04.000+00:00", - "departure" : "2009-11-17T19:05:04.000+00:00", - "lat" : 45.531503, - "lon" : -122.685357, - "name" : "NW Northrup & 14th", - "stopCode" : "10775", - "stopId" : "prt:10775", - "stopIndex" : 89, - "stopSequence" : 90, + "arrival" : "2009-11-17T18:40:00.000+00:00", + "departure" : "2009-11-17T18:40:00.000+00:00", + "lat" : 45.523097, + "lon" : -122.690083, + "name" : "W Burnside & NW 19th", + "stopCode" : "735", + "stopId" : "prt:735", + "stopIndex" : 104, + "stopSequence" : 105, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:06:07.000+00:00", - "departure" : "2009-11-17T19:06:07.000+00:00", - "lat" : 45.531434, - "lon" : -122.689417, - "name" : "NW Northrup & 18th", - "stopCode" : "10776", - "stopId" : "prt:10776", - "stopIndex" : 90, - "stopSequence" : 91, + "arrival" : "2009-11-17T18:40:27.000+00:00", + "departure" : "2009-11-17T18:40:27.000+00:00", + "lat" : 45.523176, + "lon" : -122.692139, + "name" : "W Burnside & NW 20th", + "stopCode" : "741", + "stopId" : "prt:741", + "stopIndex" : 105, + "stopSequence" : 106, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:07:24.000+00:00", - "departure" : "2009-11-17T19:07:24.000+00:00", - "lat" : 45.531346, - "lon" : -122.694455, - "name" : "NW Northrup & 21st", - "stopCode" : "10777", - "stopId" : "prt:10777", - "stopIndex" : 91, - "stopSequence" : 92, + "arrival" : "2009-11-17T18:40:40.000+00:00", + "departure" : "2009-11-17T18:40:40.000+00:00", + "lat" : 45.52322, + "lon" : -122.69313, + "name" : "W Burnside & NW 20th Pl", + "stopCode" : "742", + "stopId" : "prt:742", + "stopIndex" : 106, + "stopSequence" : 107, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 104, - "points" : "yz{tG`zskV?tBCfD???^?nE?V@Z?PH\\Nb@`@~@Rf@??`@bANb@FV@R?P?pE?jA@h@AnAbBl@LFJN\\f@LT??NXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ" + "length" : 94, + "points" : "coztGd}qkVNl@r@`CZhA`A`D??Ph@l@tBb@rARh@Pd@???BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Broadway/Halsey", - "routeId" : "prt:77", - "routeLongName" : "Broadway/Halsey", - "routeShortName" : "77", + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:54:29.000+00:00", + "startTime" : "2009-11-17T18:27:58.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T19:07:55.000+00:00", - "departure" : "2009-11-17T19:07:55.000+00:00", - "lat" : 45.531308, - "lon" : -122.696445, - "name" : "NW Northrup & 22nd", - "stopCode" : "10778", - "stopId" : "prt:10778", - "stopIndex" : 92, - "stopSequence" : 93, + "arrival" : "2009-11-17T18:41:03.000+00:00", + "departure" : "2009-11-17T18:41:03.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 107, + "stopSequence" : 108, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "7702", - "tripId" : "prt:771W1180" + "tripBlockId" : "2071", + "tripId" : "prt:200W1210" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 18.81, - "endTime" : "2009-11-17T19:08:19.000+00:00", + "distance" : 999.1, + "endTime" : "2009-11-17T18:53:55.000+00:00", "from" : { - "arrival" : "2009-11-17T19:07:55.000+00:00", - "departure" : "2009-11-17T19:07:55.000+00:00", - "lat" : 45.531308, - "lon" : -122.696445, - "name" : "NW Northrup & 22nd", - "stopCode" : "10778", - "stopId" : "prt:10778", + "arrival" : "2009-11-17T18:41:03.000+00:00", + "departure" : "2009-11-17T18:41:03.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 37, + "generalizedCost" : 1511, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 7, - "points" : "sy{tGxc{kV???LABF?B??J" + "length" : 29, + "points" : "ugztGdzzkVL?ATClAI|DK?G?mCBkCDoCDmCBoCDkCBoCB[?sBD]?Y@eA@K?C?K?W@{A@M@C@I?_CB?G" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T19:07:55.000+00:00", + "startTime" : "2009-11-17T18:41:03.000+00:00", "steps" : [ { "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 18.81, + "distance" : 113.27, "elevation" : "", - "lat" : 45.5313019, - "lon" : -122.6964448, + "lat" : 45.5232491, + "lon" : -122.6949067, "relativeDirection" : "DEPART", "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 882.16, + "elevation" : "", + "lat" : 45.5233204, + "lon" : -122.696357, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Northwest 22nd Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "EAST", + "area" : false, + "bogusName" : false, + "distance" : 3.68, + "elevation" : "", + "lat" : 45.5312508, + "lon" : -122.6966386, + "relativeDirection" : "RIGHT", + "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T19:08:19.000+00:00", + "arrival" : "2009-11-17T18:53:55.000+00:00", "lat" : 45.53122, "lon" : -122.69659, "name" : "NW Northrup St. & NW 22nd Ave. (P2)", @@ -2705,14 +2744,14 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:37:30.000+00:00", + "startTime" : "2009-11-17T18:16:00.000+00:00", "tooSloped" : false, - "transfers" : 1, - "transitTime" : 1293, - "waitingTime" : 208, - "walkDistance" : 438.43, + "transfers" : 0, + "transitTime" : 785, + "waitingTime" : 0, + "walkDistance" : 1919.43, "walkLimitExceeded" : false, - "walkTime" : 348 + "walkTime" : 1490 } ] ] @@ -3836,10 +3875,10 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1553, + "duration" : 2260, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T19:09:40.000+00:00", + "endTime" : "2009-11-17T19:07:27.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -3863,19 +3902,39 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ] + }, + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] } ] }, - "generalizedCost" : 2895, + "generalizedCost" : 4058, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, "distance" : 87.02, - "endTime" : "2009-11-17T18:45:00.000+00:00", + "endTime" : "2009-11-17T18:31:00.000+00:00", "from" : { - "departure" : "2009-11-17T18:43:47.000+00:00", + "departure" : "2009-11-17T18:29:47.000+00:00", "lat" : 45.52337, "lon" : -122.653725, "name" : "NE 12th & Couch", @@ -3895,7 +3954,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:43:47.000+00:00", + "startTime" : "2009-11-17T18:29:47.000+00:00", "steps" : [ { "absoluteDirection" : "SOUTH", @@ -3925,8 +3984,8 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ], "to" : { - "arrival" : "2009-11-17T18:45:00.000+00:00", - "departure" : "2009-11-17T18:45:00.000+00:00", + "arrival" : "2009-11-17T18:31:00.000+00:00", + "departure" : "2009-11-17T18:31:00.000+00:00", "lat" : 45.523103, "lon" : -122.653064, "name" : "NE Sandy & 12th", @@ -3945,338 +4004,493 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 3729.97, - "endTime" : "2009-11-17T18:57:49.000+00:00", + "distance" : 1883.59, + "endTime" : "2009-11-17T18:38:19.000+00:00", "from" : { - "arrival" : "2009-11-17T18:45:00.000+00:00", - "departure" : "2009-11-17T18:45:00.000+00:00", + "arrival" : "2009-11-17T18:31:00.000+00:00", + "departure" : "2009-11-17T18:31:00.000+00:00", "lat" : 45.523103, "lon" : -122.653064, "name" : "NE Sandy & 12th", "stopCode" : "5055", "stopId" : "prt:5055", - "stopIndex" : 94, - "stopSequence" : 95, + "stopIndex" : 84, + "stopSequence" : 85, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1369, - "headsign" : "Beaverton TC", + "generalizedCost" : 1039, + "headsign" : "Sherwood via Portland city center", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:45:47.000+00:00", - "departure" : "2009-11-17T18:45:47.000+00:00", + "arrival" : "2009-11-17T18:31:47.000+00:00", + "departure" : "2009-11-17T18:31:47.000+00:00", "lat" : 45.523024, "lon" : -122.656526, "name" : "E Burnside & NE 9th", "stopCode" : "819", "stopId" : "prt:819", - "stopIndex" : 95, - "stopSequence" : 96, + "stopIndex" : 85, + "stopSequence" : 86, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:46:24.000+00:00", - "departure" : "2009-11-17T18:46:24.000+00:00", + "arrival" : "2009-11-17T18:32:24.000+00:00", + "departure" : "2009-11-17T18:32:24.000+00:00", "lat" : 45.523012, "lon" : -122.659365, "name" : "E Burnside & NE 6th", "stopCode" : "805", "stopId" : "prt:805", - "stopIndex" : 96, - "stopSequence" : 97, + "stopIndex" : 86, + "stopSequence" : 87, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T18:32:52.000+00:00", + "departure" : "2009-11-17T18:32:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 87, + "stopSequence" : 88, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:46:52.000+00:00", - "departure" : "2009-11-17T18:46:52.000+00:00", - "lat" : 45.523015, - "lon" : -122.661534, - "name" : "E Burnside & NE M L King", - "stopCode" : "705", - "stopId" : "prt:705", - "stopIndex" : 97, - "stopSequence" : 98, - "vertexType" : "TRANSIT", - "zoneId" : "1" + "arrival" : "2009-11-17T18:35:00.000+00:00", + "departure" : "2009-11-17T18:35:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 88, + "stopSequence" : 89, + "vertexType" : "TRANSIT", + "zoneId" : "0" + } + ], + "legGeometry" : { + "length" : 43, + "points" : "weztGdtrkV?BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE\\CPBt@ZvAl@d@R" + }, + "mode" : "BUS", + "pathway" : false, + "realTime" : false, + "route" : "Barbur/Sandy Blvd", + "routeId" : "prt:12", + "routeLongName" : "Barbur/Sandy Blvd", + "routeShortName" : "12", + "routeType" : 3, + "serviceDate" : "2009-11-17", + "startTime" : "2009-11-17T18:31:00.000+00:00", + "steps" : [ ], + "to" : { + "arrival" : "2009-11-17T18:38:19.000+00:00", + "departure" : "2009-11-17T18:38:19.000+00:00", + "lat" : 45.521958, + "lon" : -122.675956, + "name" : "SW 5th & Pine", + "stopCode" : "7631", + "stopId" : "prt:7631", + "stopIndex" : 89, + "stopSequence" : 90, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : true, + "tripBlockId" : "1235", + "tripId" : "prt:120W1270" + }, + { + "agencyTimeZoneOffset" : -28800000, + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 534.33, + "endTime" : "2009-11-17T18:45:25.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:38:19.000+00:00", + "departure" : "2009-11-17T18:38:19.000+00:00", + "lat" : 45.521958, + "lon" : -122.675956, + "name" : "SW 5th & Pine", + "stopCode" : "7631", + "stopId" : "prt:7631", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 820, + "interlineWithPreviousLeg" : false, + "legGeometry" : { + "length" : 24, + "points" : "e_ztGvcwkVADlAl@NFi@|CAFCJCJCNi@|CCRGXm@lDdCfA]hBo@rDCPADL@H@F@JB@@@K" + }, + "mode" : "WALK", + "pathway" : false, + "realTime" : false, + "rentedBike" : false, + "route" : "", + "startTime" : "2009-11-17T18:38:19.000+00:00", + "steps" : [ + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 47.2, + "elevation" : "", + "lat" : 45.5219669, + "lon" : -122.6759883, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "Southwest 5th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : true, + "distance" : 9.01, + "elevation" : "", + "lat" : 45.5215719, + "lon" : -122.67621, + "relativeDirection" : "CONTINUE", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 243.52, + "elevation" : "", + "lat" : 45.5214961, + "lon" : -122.676251, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southwest Oak Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 79.59, + "elevation" : "", + "lat" : 45.5222784, + "lon" : -122.6791704, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southwest Park Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 129.53, + "elevation" : "", + "lat" : 45.5216085, + "lon" : -122.6795303, + "relativeDirection" : "RIGHT", + "stayOn" : false, + "streetName" : "Southwest Stark Street", + "walkingBike" : false }, { - "arrival" : "2009-11-17T18:49:00.000+00:00", - "departure" : "2009-11-17T18:49:00.000+00:00", - "lat" : 45.523249, - "lon" : -122.671269, - "name" : "W Burnside & Burnside Bridge", - "stopCode" : "689", - "stopId" : "prt:689", - "stopIndex" : 98, - "stopSequence" : 99, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, + "absoluteDirection" : "SOUTH", + "area" : false, + "bogusName" : false, + "distance" : 25.48, + "elevation" : "", + "lat" : 45.5220244, + "lon" : -122.6810834, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Southwest 10th Avenue", + "walkingBike" : false + } + ], + "to" : { + "arrival" : "2009-11-17T18:45:25.000+00:00", + "departure" : "2009-11-17T18:48:09.000+00:00", + "lat" : 45.521786, + "lon" : -122.68109, + "name" : "SW 10th & Stark", + "stopCode" : "10769", + "stopId" : "prt:10769", + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "transitLeg" : false, + "walkingBike" : false + }, + { + "agencyId" : "prt:prt", + "agencyName" : "TriMet", + "agencyTimeZoneOffset" : -28800000, + "agencyUrl" : "http://trimet.org", + "arrivalDelay" : 0, + "departureDelay" : 0, + "distance" : 2493.24, + "endTime" : "2009-11-17T19:05:00.000+00:00", + "from" : { + "arrival" : "2009-11-17T18:45:25.000+00:00", + "departure" : "2009-11-17T18:48:09.000+00:00", + "lat" : 45.521786, + "lon" : -122.68109, + "name" : "SW 10th & Stark", + "stopCode" : "10769", + "stopId" : "prt:10769", + "stopIndex" : 12, + "stopSequence" : 13, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + "generalizedCost" : 1775, + "headsign" : "NW 23rd Ave", + "interlineWithPreviousLeg" : false, + "intermediateStops" : [ { - "arrival" : "2009-11-17T18:50:00.000+00:00", - "departure" : "2009-11-17T18:50:00.000+00:00", - "lat" : 45.523169, - "lon" : -122.675893, - "name" : "W Burnside & NW 5th", - "stopCode" : "782", - "stopId" : "prt:782", - "stopIndex" : 99, - "stopSequence" : 100, + "arrival" : "2009-11-17T18:49:35.000+00:00", + "departure" : "2009-11-17T18:49:35.000+00:00", + "lat" : 45.523593, + "lon" : -122.681083, + "name" : "NW 10th & Couch", + "stopCode" : "10770", + "stopId" : "prt:10770", + "stopIndex" : 13, + "stopSequence" : 14, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:51:17.000+00:00", - "departure" : "2009-11-17T18:51:17.000+00:00", - "lat" : 45.523115, - "lon" : -122.678939, - "name" : "W Burnside & NW Park", - "stopCode" : "716", - "stopId" : "prt:716", - "stopIndex" : 100, - "stopSequence" : 101, + "arrival" : "2009-11-17T18:50:41.000+00:00", + "departure" : "2009-11-17T18:50:41.000+00:00", + "lat" : 45.525011, + "lon" : -122.681113, + "name" : "NW 10th & Everett", + "stopCode" : "10771", + "stopId" : "prt:10771", + "stopIndex" : 14, + "stopSequence" : 15, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:52:25.000+00:00", - "departure" : "2009-11-17T18:52:25.000+00:00", - "lat" : 45.523048, - "lon" : -122.681606, - "name" : "W Burnside & NW 10th", - "stopCode" : "10791", - "stopId" : "prt:10791", - "stopIndex" : 101, - "stopSequence" : 102, + "arrival" : "2009-11-17T18:51:49.000+00:00", + "departure" : "2009-11-17T18:51:49.000+00:00", + "lat" : 45.526446, + "lon" : -122.68118, + "name" : "NW 10th & Glisan", + "stopCode" : "10772", + "stopId" : "prt:10772", + "stopIndex" : 15, + "stopSequence" : 16, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:53:14.000+00:00", - "departure" : "2009-11-17T18:53:14.000+00:00", - "lat" : 45.523, - "lon" : -122.683535, - "name" : "W Burnside & NW 12th", - "stopCode" : "11032", - "stopId" : "prt:11032", - "stopIndex" : 102, - "stopSequence" : 103, + "arrival" : "2009-11-17T18:53:30.000+00:00", + "departure" : "2009-11-17T18:53:30.000+00:00", + "lat" : 45.528572, + "lon" : -122.68125, + "name" : "NW 10th & Johnson", + "stopCode" : "10773", + "stopId" : "prt:10773", + "stopIndex" : 16, + "stopSequence" : 17, "vertexType" : "TRANSIT", - "zoneId" : "0" + "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:55:09.000+00:00", - "departure" : "2009-11-17T18:55:09.000+00:00", - "lat" : 45.522985, - "lon" : -122.688091, - "name" : "W Burnside & NW 17th", - "stopCode" : "10809", - "stopId" : "prt:10809", - "stopIndex" : 103, - "stopSequence" : 104, + "arrival" : "2009-11-17T18:55:11.000+00:00", + "departure" : "2009-11-17T18:55:11.000+00:00", + "lat" : 45.530707, + "lon" : -122.68132, + "name" : "NW 10th & Marshall", + "stopCode" : "10774", + "stopId" : "prt:10774", + "stopIndex" : 17, + "stopSequence" : 18, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:56:00.000+00:00", - "departure" : "2009-11-17T18:56:00.000+00:00", - "lat" : 45.523097, - "lon" : -122.690083, - "name" : "W Burnside & NW 19th", - "stopCode" : "735", - "stopId" : "prt:735", - "stopIndex" : 104, - "stopSequence" : 105, + "arrival" : "2009-11-17T18:56:52.000+00:00", + "departure" : "2009-11-17T18:56:52.000+00:00", + "lat" : 45.531534, + "lon" : -122.683319, + "name" : "NW 12th & Northrup", + "stopCode" : "12796", + "stopId" : "prt:12796", + "stopIndex" : 18, + "stopSequence" : 19, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:56:21.000+00:00", - "departure" : "2009-11-17T18:56:21.000+00:00", - "lat" : 45.523176, - "lon" : -122.692139, - "name" : "W Burnside & NW 20th", - "stopCode" : "741", - "stopId" : "prt:741", - "stopIndex" : 105, - "stopSequence" : 106, + "arrival" : "2009-11-17T18:58:00.000+00:00", + "departure" : "2009-11-17T19:00:00.000+00:00", + "lat" : 45.531503, + "lon" : -122.685357, + "name" : "NW Northrup & 14th", + "stopCode" : "10775", + "stopId" : "prt:10775", + "stopIndex" : 19, + "stopSequence" : 20, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:56:31.000+00:00", - "departure" : "2009-11-17T18:56:31.000+00:00", - "lat" : 45.52322, - "lon" : -122.69313, - "name" : "W Burnside & NW 20th Pl", - "stopCode" : "742", - "stopId" : "prt:742", - "stopIndex" : 106, - "stopSequence" : 107, + "arrival" : "2009-11-17T19:01:26.000+00:00", + "departure" : "2009-11-17T19:01:26.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 20, + "stopSequence" : 21, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:56:49.000+00:00", - "departure" : "2009-11-17T18:56:49.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 107, - "stopSequence" : 108, + "arrival" : "2009-11-17T19:03:13.000+00:00", + "departure" : "2009-11-17T19:03:13.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 21, + "stopSequence" : 22, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:57:22.000+00:00", - "departure" : "2009-11-17T18:57:22.000+00:00", - "lat" : 45.523512, - "lon" : -122.698081, - "name" : "W Burnside & NW 23rd", - "stopCode" : "755", - "stopId" : "prt:755", - "stopIndex" : 108, - "stopSequence" : 109, + "arrival" : "2009-11-17T19:03:55.000+00:00", + "departure" : "2009-11-17T19:03:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 22, + "stopSequence" : 23, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 95, - "points" : "weztGdtrkV?BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APu@lJMhBI`@" + "length" : 55, + "points" : "e~ytGbdxkV[OQ@{CFa@B{B@??S@mC@Q@eB@??U@mCB{BD??S?mCBmCFyBB??U?kCBsCDsBD??W?kCBBlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ???d@FtKvBE" }, - "mode" : "BUS", + "mode" : "TRAM", "pathway" : false, "realTime" : false, - "route" : "Burnside/Stark", - "routeId" : "prt:20", - "routeLongName" : "Burnside/Stark", - "routeShortName" : "20", - "routeType" : 3, + "route" : "Portland Streetcar", + "routeId" : "prt:193", + "routeLongName" : "Portland Streetcar", + "routeType" : 0, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:45:00.000+00:00", + "startTime" : "2009-11-17T18:48:09.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T18:57:49.000+00:00", - "departure" : "2009-11-17T18:57:49.000+00:00", - "lat" : 45.523897, - "lon" : -122.700681, - "name" : "W Burnside & NW 23rd Pl", - "stopCode" : "9555", - "stopId" : "prt:9555", - "stopIndex" : 109, - "stopSequence" : 110, + "arrival" : "2009-11-17T19:05:00.000+00:00", + "departure" : "2009-11-17T19:05:00.000+00:00", + "lat" : 45.530612, + "lon" : -122.698688, + "name" : "NW 23rd & Marshall", + "stopCode" : "8989", + "stopId" : "prt:8989", + "stopIndex" : 23, + "stopSequence" : 24, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "2037", - "tripId" : "prt:200W1220" + "tripBlockId" : "9380", + "tripId" : "prt:1930W1240" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 913.81, - "endTime" : "2009-11-17T19:09:40.000+00:00", + "distance" : 187.24, + "endTime" : "2009-11-17T19:07:27.000+00:00", "from" : { - "arrival" : "2009-11-17T18:57:49.000+00:00", - "departure" : "2009-11-17T18:57:49.000+00:00", - "lat" : 45.523897, - "lon" : -122.700681, - "name" : "W Burnside & NW 23rd Pl", - "stopCode" : "9555", - "stopId" : "prt:9555", + "arrival" : "2009-11-17T19:05:00.000+00:00", + "departure" : "2009-11-17T19:05:00.000+00:00", + "lat" : 45.530612, + "lon" : -122.698688, + "name" : "NW 23rd & Marshall", + "stopCode" : "8989", + "stopId" : "prt:8989", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1388, + "generalizedCost" : 286, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 39, - "points" : "ikztGh~{kVNDEVUzACPOUQO_@Yc@[QMMEOKOIECGCIAMCGCGAECECECOOMOGKIFMLk@BsABGDGBoCBkCDoC@mCBmCDoCDmCD?qA" + "length" : 7, + "points" : "iu{tGxq{kV?DK?GBwADK?DpH" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:57:49.000+00:00", + "startTime" : "2009-11-17T19:05:00.000+00:00", "steps" : [ { - "absoluteDirection" : "WEST", + "absoluteDirection" : "NORTH", "area" : false, "bogusName" : false, - "distance" : 54.63, + "distance" : 60.76, "elevation" : "", - "lat" : 45.5238156, - "lon" : -122.7007199, + "lat" : 45.5306118, + "lon" : -122.6987102, "relativeDirection" : "DEPART", "stayOn" : false, - "streetName" : "West Burnside Street", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTHEAST", - "area" : false, - "bogusName" : false, - "distance" : 176.41, - "elevation" : "", - "lat" : 45.5239733, - "lon" : -122.701384, - "relativeDirection" : "RIGHT", - "stayOn" : false, - "streetName" : "Northwest 24th Place", - "walkingBike" : false - }, - { - "absoluteDirection" : "NORTHWEST", - "area" : false, - "bogusName" : false, - "distance" : 15.26, - "elevation" : "", - "lat" : 45.5253583, - "lon" : -122.7003357, - "relativeDirection" : "LEFT", - "stayOn" : false, - "streetName" : "Northwest Westover Road", + "streetName" : "Northwest 23rd Avenue", "walkingBike" : false }, { "absoluteDirection" : "NORTH", "area" : false, - "bogusName" : false, - "distance" : 635.66, + "bogusName" : true, + "distance" : 7.29, "elevation" : "", - "lat" : 45.5254724, - "lon" : -122.7004445, - "relativeDirection" : "SLIGHTLY_RIGHT", + "lat" : 45.531153, + "lon" : -122.6987606, + "relativeDirection" : "CONTINUE", "stayOn" : false, - "streetName" : "Northwest 24th Avenue", + "streetName" : "path", "walkingBike" : false }, { - "absoluteDirection" : "EAST", + "absoluteDirection" : "WEST", "area" : false, "bogusName" : false, - "distance" : 31.86, + "distance" : 119.18, "elevation" : "", - "lat" : 45.531181, - "lon" : -122.7007063, - "relativeDirection" : "RIGHT", + "lat" : 45.5312184, + "lon" : -122.698768, + "relativeDirection" : "LEFT", "stayOn" : false, "streetName" : "Northwest Northrup Street", "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T19:09:40.000+00:00", + "arrival" : "2009-11-17T19:07:27.000+00:00", "lat" : 45.531, "lon" : -122.70029, "name" : "NW Northrup St. & NW 24th Ave. (P3)", @@ -4286,21 +4500,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:43:47.000+00:00", + "startTime" : "2009-11-17T18:29:47.000+00:00", "tooSloped" : false, - "transfers" : 0, - "transitTime" : 769, - "waitingTime" : 0, - "walkDistance" : 1000.83, + "transfers" : 1, + "transitTime" : 1450, + "waitingTime" : 164, + "walkDistance" : 808.59, "walkLimitExceeded" : false, - "walkTime" : 784 + "walkTime" : 646 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1567, + "duration" : 1553, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T19:11:51.000+00:00", + "endTime" : "2009-11-17T19:09:40.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -4324,39 +4538,19 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ] - }, - { - "legIndices" : [ - 2 - ], - "products" : [ - { - "amount" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "id" : "prt:8", - "name" : "regular" - } - ] } ] }, - "generalizedCost" : 2957, + "generalizedCost" : 2895, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 20.74, - "endTime" : "2009-11-17T18:46:00.000+00:00", + "distance" : 87.02, + "endTime" : "2009-11-17T18:45:00.000+00:00", "from" : { - "departure" : "2009-11-17T18:45:44.000+00:00", + "departure" : "2009-11-17T18:43:47.000+00:00", "lat" : 45.52337, "lon" : -122.653725, "name" : "NE 12th & Couch", @@ -4365,24 +4559,24 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 33, + "generalizedCost" : 137, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 4, - "points" : "ahztGxxrkV@Sb@??W" + "length" : 9, + "points" : "ahztGxxrkV@Sb@?`@@?a@?k@GGKY@A" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:45:44.000+00:00", + "startTime" : "2009-11-17T18:43:47.000+00:00", "steps" : [ { "absoluteDirection" : "SOUTH", "area" : false, "bogusName" : false, - "distance" : 20.74, + "distance" : 39.06, "elevation" : "", "lat" : 45.5233684, "lon" : -122.6536225, @@ -4390,16 +4584,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "stayOn" : false, "streetName" : "Northeast 12th Avenue", "walkingBike" : false + }, + { + "absoluteDirection" : "EAST", + "area" : false, + "bogusName" : true, + "distance" : 47.96, + "elevation" : "", + "lat" : 45.5230172, + "lon" : -122.6536338, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "path", + "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T18:46:00.000+00:00", - "departure" : "2009-11-17T18:46:00.000+00:00", - "lat" : 45.52318, - "lon" : -122.653507, - "name" : "NE 12th & Sandy", - "stopCode" : "6592", - "stopId" : "prt:6592", + "arrival" : "2009-11-17T18:45:00.000+00:00", + "departure" : "2009-11-17T18:45:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -4413,363 +4620,330 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 1355.21, - "endTime" : "2009-11-17T18:51:01.000+00:00", + "distance" : 3729.97, + "endTime" : "2009-11-17T18:57:49.000+00:00", "from" : { - "arrival" : "2009-11-17T18:46:00.000+00:00", - "departure" : "2009-11-17T18:46:00.000+00:00", - "lat" : 45.52318, - "lon" : -122.653507, - "name" : "NE 12th & Sandy", - "stopCode" : "6592", - "stopId" : "prt:6592", - "stopIndex" : 34, - "stopSequence" : 35, + "arrival" : "2009-11-17T18:45:00.000+00:00", + "departure" : "2009-11-17T18:45:00.000+00:00", + "lat" : 45.523103, + "lon" : -122.653064, + "name" : "NE Sandy & 12th", + "stopCode" : "5055", + "stopId" : "prt:5055", + "stopIndex" : 94, + "stopSequence" : 95, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 901, - "headsign" : "Rose Qtr TC", + "generalizedCost" : 1369, + "headsign" : "Beaverton TC", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T18:47:45.000+00:00", - "departure" : "2009-11-17T18:47:45.000+00:00", - "lat" : 45.527449, - "lon" : -122.653462, - "name" : "NE 12th & Irving", - "stopCode" : "6582", - "stopId" : "prt:6582", - "stopIndex" : 35, - "stopSequence" : 36, + "arrival" : "2009-11-17T18:45:47.000+00:00", + "departure" : "2009-11-17T18:45:47.000+00:00", + "lat" : 45.523024, + "lon" : -122.656526, + "name" : "E Burnside & NE 9th", + "stopCode" : "819", + "stopId" : "prt:819", + "stopIndex" : 95, + "stopSequence" : 96, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:49:00.000+00:00", - "departure" : "2009-11-17T18:49:00.000+00:00", - "lat" : 45.529793, - "lon" : -122.654429, - "name" : "NE 11th & Holladay", - "stopCode" : "8513", - "stopId" : "prt:8513", - "stopIndex" : 36, - "stopSequence" : 37, + "arrival" : "2009-11-17T18:46:24.000+00:00", + "departure" : "2009-11-17T18:46:24.000+00:00", + "lat" : 45.523012, + "lon" : -122.659365, + "name" : "E Burnside & NE 6th", + "stopCode" : "805", + "stopId" : "prt:805", + "stopIndex" : 96, + "stopSequence" : 97, "vertexType" : "TRANSIT", - "zoneId" : "0" + "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:49:39.000+00:00", - "departure" : "2009-11-17T18:49:39.000+00:00", - "lat" : 45.53135, - "lon" : -122.654497, - "name" : "NE 11th & Multnomah", - "stopCode" : "8938", - "stopId" : "prt:8938", - "stopIndex" : 37, - "stopSequence" : 38, + "arrival" : "2009-11-17T18:46:52.000+00:00", + "departure" : "2009-11-17T18:46:52.000+00:00", + "lat" : 45.523015, + "lon" : -122.661534, + "name" : "E Burnside & NE M L King", + "stopCode" : "705", + "stopId" : "prt:705", + "stopIndex" : 97, + "stopSequence" : 98, "vertexType" : "TRANSIT", - "zoneId" : "0" + "zoneId" : "1" }, { - "arrival" : "2009-11-17T18:50:15.000+00:00", - "departure" : "2009-11-17T18:50:15.000+00:00", - "lat" : 45.531573, - "lon" : -122.656408, - "name" : "NE Multnomah & 9th", - "stopCode" : "4056", - "stopId" : "prt:4056", - "stopIndex" : 38, - "stopSequence" : 39, - "vertexType" : "TRANSIT", - "zoneId" : "0" - } - ], - "legGeometry" : { - "length" : 33, - "points" : "{fztG`xrkVwA?mCAmC?oCA}C?sDC??aBAm@@k@AY?uABU@I@IBQFb@fC}@d@OFO@q@???Q?]?gGA??[??nJ???b@?vK?rA" - }, - "mode" : "BUS", - "pathway" : false, - "realTime" : false, - "route" : "12th Ave", - "routeId" : "prt:70", - "routeLongName" : "12th Ave", - "routeShortName" : "70", - "routeType" : 3, - "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:46:00.000+00:00", - "steps" : [ ], - "to" : { - "arrival" : "2009-11-17T18:51:01.000+00:00", - "departure" : "2009-11-17T18:54:29.000+00:00", - "lat" : 45.531569, - "lon" : -122.659045, - "name" : "NE Multnomah & 7th", - "stopCode" : "4054", - "stopId" : "prt:4054", - "stopIndex" : 39, - "stopSequence" : 40, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - "transitLeg" : true, - "tripBlockId" : "7002", - "tripId" : "prt:700W1170" - }, - { - "agencyId" : "prt:prt", - "agencyName" : "TriMet", - "agencyTimeZoneOffset" : -28800000, - "agencyUrl" : "http://trimet.org", - "arrivalDelay" : 0, - "departureDelay" : 0, - "distance" : 3838.71, - "endTime" : "2009-11-17T19:08:50.000+00:00", - "from" : { - "arrival" : "2009-11-17T18:51:01.000+00:00", - "departure" : "2009-11-17T18:54:29.000+00:00", - "lat" : 45.531569, - "lon" : -122.659045, - "name" : "NE Multnomah & 7th", - "stopCode" : "4054", - "stopId" : "prt:4054", - "stopIndex" : 81, - "stopSequence" : 82, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - "generalizedCost" : 1669, - "headsign" : "Montgomery Park", - "interlineWithPreviousLeg" : false, - "intermediateStops" : [ - { - "arrival" : "2009-11-17T18:55:05.000+00:00", - "departure" : "2009-11-17T18:55:05.000+00:00", - "lat" : 45.531586, - "lon" : -122.660482, - "name" : "NE Multnomah & Grand", - "stopCode" : "4043", - "stopId" : "prt:4043", - "stopIndex" : 82, - "stopSequence" : 83, + "arrival" : "2009-11-17T18:49:00.000+00:00", + "departure" : "2009-11-17T18:49:00.000+00:00", + "lat" : 45.523249, + "lon" : -122.671269, + "name" : "W Burnside & Burnside Bridge", + "stopCode" : "689", + "stopId" : "prt:689", + "stopIndex" : 98, + "stopSequence" : 99, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:56:09.000+00:00", - "departure" : "2009-11-17T18:56:09.000+00:00", - "lat" : 45.531159, - "lon" : -122.66293, - "name" : "NE Multnomah & 3rd", - "stopCode" : "11492", - "stopId" : "prt:11492", - "stopIndex" : 83, - "stopSequence" : 84, + "arrival" : "2009-11-17T18:50:00.000+00:00", + "departure" : "2009-11-17T18:50:00.000+00:00", + "lat" : 45.523169, + "lon" : -122.675893, + "name" : "W Burnside & NW 5th", + "stopCode" : "782", + "stopId" : "prt:782", + "stopIndex" : 99, + "stopSequence" : 100, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T18:58:00.000+00:00", - "departure" : "2009-11-17T18:58:00.000+00:00", - "lat" : 45.530005, - "lon" : -122.666476, - "name" : "Rose Quarter Transit Center", - "stopCode" : "2592", - "stopId" : "prt:2592", - "stopIndex" : 84, - "stopSequence" : 85, + "arrival" : "2009-11-17T18:51:17.000+00:00", + "departure" : "2009-11-17T18:51:17.000+00:00", + "lat" : 45.523115, + "lon" : -122.678939, + "name" : "W Burnside & NW Park", + "stopCode" : "716", + "stopId" : "prt:716", + "stopIndex" : 100, + "stopSequence" : 101, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:01:20.000+00:00", - "departure" : "2009-11-17T19:01:20.000+00:00", - "lat" : 45.526655, - "lon" : -122.676462, - "name" : "NW Glisan & 6th", - "stopCode" : "10803", - "stopId" : "prt:10803", - "stopIndex" : 85, - "stopSequence" : 86, + "arrival" : "2009-11-17T18:52:25.000+00:00", + "departure" : "2009-11-17T18:52:25.000+00:00", + "lat" : 45.523048, + "lon" : -122.681606, + "name" : "W Burnside & NW 10th", + "stopCode" : "10791", + "stopId" : "prt:10791", + "stopIndex" : 101, + "stopSequence" : 102, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:02:15.000+00:00", - "departure" : "2009-11-17T19:02:15.000+00:00", - "lat" : 45.528799, - "lon" : -122.677238, - "name" : "NW Station Way & Union Station", - "stopCode" : "12801", - "stopId" : "prt:12801", - "stopIndex" : 86, - "stopSequence" : 87, + "arrival" : "2009-11-17T18:53:14.000+00:00", + "departure" : "2009-11-17T18:53:14.000+00:00", + "lat" : 45.523, + "lon" : -122.683535, + "name" : "W Burnside & NW 12th", + "stopCode" : "11032", + "stopId" : "prt:11032", + "stopIndex" : 102, + "stopSequence" : 103, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:04:00.000+00:00", - "departure" : "2009-11-17T19:04:00.000+00:00", - "lat" : 45.531582, - "lon" : -122.681193, - "name" : "NW Northrup & 10th", - "stopCode" : "12802", - "stopId" : "prt:12802", - "stopIndex" : 87, - "stopSequence" : 88, + "arrival" : "2009-11-17T18:55:09.000+00:00", + "departure" : "2009-11-17T18:55:09.000+00:00", + "lat" : 45.522985, + "lon" : -122.688091, + "name" : "W Burnside & NW 17th", + "stopCode" : "10809", + "stopId" : "prt:10809", + "stopIndex" : 103, + "stopSequence" : 104, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:04:33.000+00:00", - "departure" : "2009-11-17T19:04:33.000+00:00", - "lat" : 45.531534, - "lon" : -122.683319, - "name" : "NW 12th & Northrup", - "stopCode" : "12796", - "stopId" : "prt:12796", - "stopIndex" : 88, - "stopSequence" : 89, + "arrival" : "2009-11-17T18:56:00.000+00:00", + "departure" : "2009-11-17T18:56:00.000+00:00", + "lat" : 45.523097, + "lon" : -122.690083, + "name" : "W Burnside & NW 19th", + "stopCode" : "735", + "stopId" : "prt:735", + "stopIndex" : 104, + "stopSequence" : 105, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:05:04.000+00:00", - "departure" : "2009-11-17T19:05:04.000+00:00", - "lat" : 45.531503, - "lon" : -122.685357, - "name" : "NW Northrup & 14th", - "stopCode" : "10775", - "stopId" : "prt:10775", - "stopIndex" : 89, - "stopSequence" : 90, + "arrival" : "2009-11-17T18:56:21.000+00:00", + "departure" : "2009-11-17T18:56:21.000+00:00", + "lat" : 45.523176, + "lon" : -122.692139, + "name" : "W Burnside & NW 20th", + "stopCode" : "741", + "stopId" : "prt:741", + "stopIndex" : 105, + "stopSequence" : 106, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:06:07.000+00:00", - "departure" : "2009-11-17T19:06:07.000+00:00", - "lat" : 45.531434, - "lon" : -122.689417, - "name" : "NW Northrup & 18th", - "stopCode" : "10776", - "stopId" : "prt:10776", - "stopIndex" : 90, - "stopSequence" : 91, + "arrival" : "2009-11-17T18:56:31.000+00:00", + "departure" : "2009-11-17T18:56:31.000+00:00", + "lat" : 45.52322, + "lon" : -122.69313, + "name" : "W Burnside & NW 20th Pl", + "stopCode" : "742", + "stopId" : "prt:742", + "stopIndex" : 106, + "stopSequence" : 107, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:07:24.000+00:00", - "departure" : "2009-11-17T19:07:24.000+00:00", - "lat" : 45.531346, - "lon" : -122.694455, - "name" : "NW Northrup & 21st", - "stopCode" : "10777", - "stopId" : "prt:10777", - "stopIndex" : 91, - "stopSequence" : 92, + "arrival" : "2009-11-17T18:56:49.000+00:00", + "departure" : "2009-11-17T18:56:49.000+00:00", + "lat" : 45.523312, + "lon" : -122.694901, + "name" : "W Burnside & NW King", + "stopCode" : "747", + "stopId" : "prt:747", + "stopIndex" : 107, + "stopSequence" : 108, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:07:55.000+00:00", - "departure" : "2009-11-17T19:07:55.000+00:00", - "lat" : 45.531308, - "lon" : -122.696445, - "name" : "NW Northrup & 22nd", - "stopCode" : "10778", - "stopId" : "prt:10778", - "stopIndex" : 92, - "stopSequence" : 93, + "arrival" : "2009-11-17T18:57:22.000+00:00", + "departure" : "2009-11-17T18:57:22.000+00:00", + "lat" : 45.523512, + "lon" : -122.698081, + "name" : "W Burnside & NW 23rd", + "stopCode" : "755", + "stopId" : "prt:755", + "stopIndex" : 108, + "stopSequence" : 109, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 109, - "points" : "yz{tG`zskV?tBCfD???^?nE?V@Z?PH\\Nb@`@~@Rf@??`@bANb@FV@R?P?pE?jA@h@AnAbBl@LFJN\\f@LT??NXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ???d@FtKmCBo@@" + "length" : 95, + "points" : "weztGdtrkV?BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC??APu@lJMhBI`@" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Broadway/Halsey", - "routeId" : "prt:77", - "routeLongName" : "Broadway/Halsey", - "routeShortName" : "77", + "route" : "Burnside/Stark", + "routeId" : "prt:20", + "routeLongName" : "Burnside/Stark", + "routeShortName" : "20", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T18:54:29.000+00:00", + "startTime" : "2009-11-17T18:45:00.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T19:08:50.000+00:00", - "departure" : "2009-11-17T19:08:50.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", - "stopIndex" : 93, - "stopSequence" : 94, + "arrival" : "2009-11-17T18:57:49.000+00:00", + "departure" : "2009-11-17T18:57:49.000+00:00", + "lat" : 45.523897, + "lon" : -122.700681, + "name" : "W Burnside & NW 23rd Pl", + "stopCode" : "9555", + "stopId" : "prt:9555", + "stopIndex" : 109, + "stopSequence" : 110, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "7702", - "tripId" : "prt:771W1180" + "tripBlockId" : "2037", + "tripId" : "prt:200W1220" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 231.46, - "endTime" : "2009-11-17T19:11:51.000+00:00", + "distance" : 913.81, + "endTime" : "2009-11-17T19:09:40.000+00:00", "from" : { - "arrival" : "2009-11-17T19:08:50.000+00:00", - "departure" : "2009-11-17T19:08:50.000+00:00", - "lat" : 45.532159, - "lon" : -122.698634, - "name" : "NW 23rd & Overton", - "stopCode" : "8981", - "stopId" : "prt:8981", + "arrival" : "2009-11-17T18:57:49.000+00:00", + "departure" : "2009-11-17T18:57:49.000+00:00", + "lat" : 45.523897, + "lon" : -122.700681, + "name" : "W Burnside & NW 23rd Pl", + "stopCode" : "9555", + "stopId" : "prt:9555", "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 353, + "generalizedCost" : 1388, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 10, - "points" : "}~{tGnq{kV?LVAF?J?L?rBCLA?RDpH" + "length" : 39, + "points" : "ikztGh~{kVNDEVUzACPOUQO_@Yc@[QMMEOKOIECGCIAMCGCGAECECECOOMOGKIFMLk@BsABGDGBoCBkCDoC@mCBmCDoCDmCD?qA" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T19:08:50.000+00:00", + "startTime" : "2009-11-17T18:57:49.000+00:00", "steps" : [ { - "absoluteDirection" : "SOUTH", + "absoluteDirection" : "WEST", + "area" : false, + "bogusName" : false, + "distance" : 54.63, + "elevation" : "", + "lat" : 45.5238156, + "lon" : -122.7007199, + "relativeDirection" : "DEPART", + "stayOn" : false, + "streetName" : "West Burnside Street", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTHEAST", "area" : false, "bogusName" : false, - "distance" : 104.46, + "distance" : 176.41, "elevation" : "", - "lat" : 45.5321578, - "lon" : -122.6987026, - "relativeDirection" : "DEPART", + "lat" : 45.5239733, + "lon" : -122.701384, + "relativeDirection" : "RIGHT", "stayOn" : false, - "streetName" : "Northwest 23rd Avenue", + "streetName" : "Northwest 24th Place", "walkingBike" : false }, { - "absoluteDirection" : "WEST", + "absoluteDirection" : "NORTHWEST", "area" : false, "bogusName" : false, - "distance" : 127.01, + "distance" : 15.26, "elevation" : "", - "lat" : 45.5312188, - "lon" : -122.6986675, + "lat" : 45.5253583, + "lon" : -122.7003357, + "relativeDirection" : "LEFT", + "stayOn" : false, + "streetName" : "Northwest Westover Road", + "walkingBike" : false + }, + { + "absoluteDirection" : "NORTH", + "area" : false, + "bogusName" : false, + "distance" : 635.66, + "elevation" : "", + "lat" : 45.5254724, + "lon" : -122.7004445, + "relativeDirection" : "SLIGHTLY_RIGHT", + "stayOn" : false, + "streetName" : "Northwest 24th Avenue", + "walkingBike" : false + }, + { + "absoluteDirection" : "EAST", + "area" : false, + "bogusName" : false, + "distance" : 31.86, + "elevation" : "", + "lat" : 45.531181, + "lon" : -122.7007063, "relativeDirection" : "RIGHT", "stayOn" : false, "streetName" : "Northwest Northrup Street", @@ -4777,7 +4951,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ], "to" : { - "arrival" : "2009-11-17T19:11:51.000+00:00", + "arrival" : "2009-11-17T19:09:40.000+00:00", "lat" : 45.531, "lon" : -122.70029, "name" : "NW Northrup St. & NW 24th Ave. (P3)", @@ -4787,21 +4961,21 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:45:44.000+00:00", + "startTime" : "2009-11-17T18:43:47.000+00:00", "tooSloped" : false, - "transfers" : 1, - "transitTime" : 1162, - "waitingTime" : 208, - "walkDistance" : 252.2, + "transfers" : 0, + "transitTime" : 769, + "waitingTime" : 0, + "walkDistance" : 1000.83, "walkLimitExceeded" : false, - "walkTime" : 197 + "walkTime" : 784 }, { "arrivedAtDestinationWithRentedBicycle" : false, - "duration" : 1578, + "duration" : 1567, "elevationGained" : 0.0, "elevationLost" : 0.0, - "endTime" : "2009-11-17T19:25:05.000+00:00", + "endTime" : "2009-11-17T19:11:51.000+00:00", "fare" : { "details" : { }, "fare" : { }, @@ -4848,16 +5022,16 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ] }, - "generalizedCost" : 3015, + "generalizedCost" : 2957, "legs" : [ { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 87.02, - "endTime" : "2009-11-17T19:00:00.000+00:00", + "distance" : 20.74, + "endTime" : "2009-11-17T18:46:00.000+00:00", "from" : { - "departure" : "2009-11-17T18:58:47.000+00:00", + "departure" : "2009-11-17T18:45:44.000+00:00", "lat" : 45.52337, "lon" : -122.653725, "name" : "NE 12th & Couch", @@ -4866,24 +5040,24 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 137, + "generalizedCost" : 33, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 9, - "points" : "ahztGxxrkV@Sb@?`@@?a@?k@GGKY@A" + "length" : 4, + "points" : "ahztGxxrkV@Sb@??W" }, "mode" : "WALK", "pathway" : false, "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T18:58:47.000+00:00", + "startTime" : "2009-11-17T18:45:44.000+00:00", "steps" : [ { "absoluteDirection" : "SOUTH", "area" : false, "bogusName" : false, - "distance" : 39.06, + "distance" : 20.74, "elevation" : "", "lat" : 45.5233684, "lon" : -122.6536225, @@ -4891,29 +5065,16 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "stayOn" : false, "streetName" : "Northeast 12th Avenue", "walkingBike" : false - }, - { - "absoluteDirection" : "EAST", - "area" : false, - "bogusName" : true, - "distance" : 47.96, - "elevation" : "", - "lat" : 45.5230172, - "lon" : -122.6536338, - "relativeDirection" : "LEFT", - "stayOn" : false, - "streetName" : "path", - "walkingBike" : false } ], "to" : { - "arrival" : "2009-11-17T19:00:00.000+00:00", - "departure" : "2009-11-17T19:00:00.000+00:00", - "lat" : 45.523103, - "lon" : -122.653064, - "name" : "NE Sandy & 12th", - "stopCode" : "5055", - "stopId" : "prt:5055", + "arrival" : "2009-11-17T18:46:00.000+00:00", + "departure" : "2009-11-17T18:46:00.000+00:00", + "lat" : 45.52318, + "lon" : -122.653507, + "name" : "NE 12th & Sandy", + "stopCode" : "6592", + "stopId" : "prt:6592", "vertexType" : "TRANSIT", "zoneId" : "1" }, @@ -4927,226 +5088,109 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 3520.32, - "endTime" : "2009-11-17T19:12:44.000+00:00", + "distance" : 1355.21, + "endTime" : "2009-11-17T18:51:01.000+00:00", "from" : { - "arrival" : "2009-11-17T19:00:00.000+00:00", - "departure" : "2009-11-17T19:00:00.000+00:00", - "lat" : 45.523103, - "lon" : -122.653064, - "name" : "NE Sandy & 12th", - "stopCode" : "5055", - "stopId" : "prt:5055", - "stopIndex" : 94, - "stopSequence" : 95, + "arrival" : "2009-11-17T18:46:00.000+00:00", + "departure" : "2009-11-17T18:46:00.000+00:00", + "lat" : 45.52318, + "lon" : -122.653507, + "name" : "NE 12th & Sandy", + "stopCode" : "6592", + "stopId" : "prt:6592", + "stopIndex" : 34, + "stopSequence" : 35, "vertexType" : "TRANSIT", "zoneId" : "1" }, - "generalizedCost" : 1364, - "headsign" : "23rd Ave to Tichner", + "generalizedCost" : 901, + "headsign" : "Rose Qtr TC", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T19:00:47.000+00:00", - "departure" : "2009-11-17T19:00:47.000+00:00", - "lat" : 45.523024, - "lon" : -122.656526, - "name" : "E Burnside & NE 9th", - "stopCode" : "819", - "stopId" : "prt:819", - "stopIndex" : 95, - "stopSequence" : 96, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:01:24.000+00:00", - "departure" : "2009-11-17T19:01:24.000+00:00", - "lat" : 45.523012, - "lon" : -122.659365, - "name" : "E Burnside & NE 6th", - "stopCode" : "805", - "stopId" : "prt:805", - "stopIndex" : 96, - "stopSequence" : 97, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:01:52.000+00:00", - "departure" : "2009-11-17T19:01:52.000+00:00", - "lat" : 45.523015, - "lon" : -122.661534, - "name" : "E Burnside & NE M L King", - "stopCode" : "705", - "stopId" : "prt:705", - "stopIndex" : 97, - "stopSequence" : 98, + "arrival" : "2009-11-17T18:47:45.000+00:00", + "departure" : "2009-11-17T18:47:45.000+00:00", + "lat" : 45.527449, + "lon" : -122.653462, + "name" : "NE 12th & Irving", + "stopCode" : "6582", + "stopId" : "prt:6582", + "stopIndex" : 35, + "stopSequence" : 36, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:04:00.000+00:00", - "departure" : "2009-11-17T19:04:00.000+00:00", - "lat" : 45.523249, - "lon" : -122.671269, - "name" : "W Burnside & Burnside Bridge", - "stopCode" : "689", - "stopId" : "prt:689", - "stopIndex" : 98, - "stopSequence" : 99, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T19:05:00.000+00:00", - "departure" : "2009-11-17T19:05:00.000+00:00", - "lat" : 45.523169, - "lon" : -122.675893, - "name" : "W Burnside & NW 5th", - "stopCode" : "782", - "stopId" : "prt:782", - "stopIndex" : 99, - "stopSequence" : 100, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T19:06:17.000+00:00", - "departure" : "2009-11-17T19:06:17.000+00:00", - "lat" : 45.523115, - "lon" : -122.678939, - "name" : "W Burnside & NW Park", - "stopCode" : "716", - "stopId" : "prt:716", - "stopIndex" : 100, - "stopSequence" : 101, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T19:07:25.000+00:00", - "departure" : "2009-11-17T19:07:25.000+00:00", - "lat" : 45.523048, - "lon" : -122.681606, - "name" : "W Burnside & NW 10th", - "stopCode" : "10791", - "stopId" : "prt:10791", - "stopIndex" : 101, - "stopSequence" : 102, - "vertexType" : "TRANSIT", - "zoneId" : "0" - }, - { - "arrival" : "2009-11-17T19:08:14.000+00:00", - "departure" : "2009-11-17T19:08:14.000+00:00", - "lat" : 45.523, - "lon" : -122.683535, - "name" : "W Burnside & NW 12th", - "stopCode" : "11032", - "stopId" : "prt:11032", - "stopIndex" : 102, - "stopSequence" : 103, + "arrival" : "2009-11-17T18:49:00.000+00:00", + "departure" : "2009-11-17T18:49:00.000+00:00", + "lat" : 45.529793, + "lon" : -122.654429, + "name" : "NE 11th & Holladay", + "stopCode" : "8513", + "stopId" : "prt:8513", + "stopIndex" : 36, + "stopSequence" : 37, "vertexType" : "TRANSIT", "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:10:09.000+00:00", - "departure" : "2009-11-17T19:10:09.000+00:00", - "lat" : 45.522985, - "lon" : -122.688091, - "name" : "W Burnside & NW 17th", - "stopCode" : "10809", - "stopId" : "prt:10809", - "stopIndex" : 103, - "stopSequence" : 104, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:11:00.000+00:00", - "departure" : "2009-11-17T19:11:00.000+00:00", - "lat" : 45.523097, - "lon" : -122.690083, - "name" : "W Burnside & NW 19th", - "stopCode" : "735", - "stopId" : "prt:735", - "stopIndex" : 104, - "stopSequence" : 105, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:11:27.000+00:00", - "departure" : "2009-11-17T19:11:27.000+00:00", - "lat" : 45.523176, - "lon" : -122.692139, - "name" : "W Burnside & NW 20th", - "stopCode" : "741", - "stopId" : "prt:741", - "stopIndex" : 105, - "stopSequence" : 106, - "vertexType" : "TRANSIT", - "zoneId" : "1" - }, - { - "arrival" : "2009-11-17T19:11:40.000+00:00", - "departure" : "2009-11-17T19:11:40.000+00:00", - "lat" : 45.52322, - "lon" : -122.69313, - "name" : "W Burnside & NW 20th Pl", - "stopCode" : "742", - "stopId" : "prt:742", - "stopIndex" : 106, - "stopSequence" : 107, + "arrival" : "2009-11-17T18:49:39.000+00:00", + "departure" : "2009-11-17T18:49:39.000+00:00", + "lat" : 45.53135, + "lon" : -122.654497, + "name" : "NE 11th & Multnomah", + "stopCode" : "8938", + "stopId" : "prt:8938", + "stopIndex" : 37, + "stopSequence" : 38, "vertexType" : "TRANSIT", - "zoneId" : "1" + "zoneId" : "0" }, { - "arrival" : "2009-11-17T19:12:03.000+00:00", - "departure" : "2009-11-17T19:12:03.000+00:00", - "lat" : 45.523312, - "lon" : -122.694901, - "name" : "W Burnside & NW King", - "stopCode" : "747", - "stopId" : "prt:747", - "stopIndex" : 107, - "stopSequence" : 108, + "arrival" : "2009-11-17T18:50:15.000+00:00", + "departure" : "2009-11-17T18:50:15.000+00:00", + "lat" : 45.531573, + "lon" : -122.656408, + "name" : "NE Multnomah & 9th", + "stopCode" : "4056", + "stopId" : "prt:4056", + "stopIndex" : 38, + "stopSequence" : 39, "vertexType" : "TRANSIT", - "zoneId" : "1" + "zoneId" : "0" } ], "legGeometry" : { - "length" : 90, - "points" : "weztGdtrkV?BPj@@jA?jEAhE?pD???VAjE?hE?dB?b@???`AAhE?dD???l@C`EAhEEhE?bAA|@?XAZ@\\AzACnGKbKAjC?bE???JEnE@fEDlE@hE@~A??@rBBzDBpE@~A???Z@tD@RBnEB|A???@BdB?lEBjA??BnBApF@dB?X?^@r@?f@@bCAx@EtB???VChAE|BGnD??AXKnEGnD???XGjD??AZEfCC`AEzB??AXCfAGxDE|AEtBIlC" + "length" : 33, + "points" : "{fztG`xrkVwA?mCAmC?oCA}C?sDC??aBAm@@k@AY?uABU@I@IBQFb@fC}@d@OFO@q@???Q?]?gGA??[??nJ???b@?vK?rA" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Burnside/Stark", - "routeId" : "prt:20", - "routeLongName" : "Burnside/Stark", - "routeShortName" : "20", + "route" : "12th Ave", + "routeId" : "prt:70", + "routeLongName" : "12th Ave", + "routeShortName" : "70", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T19:00:00.000+00:00", + "startTime" : "2009-11-17T18:46:00.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T19:12:44.000+00:00", - "departure" : "2009-11-17T19:17:51.000+00:00", - "lat" : 45.523512, - "lon" : -122.698081, - "name" : "W Burnside & NW 23rd", - "stopCode" : "755", - "stopId" : "prt:755", - "stopIndex" : 108, - "stopSequence" : 109, + "arrival" : "2009-11-17T18:51:01.000+00:00", + "departure" : "2009-11-17T18:54:29.000+00:00", + "lat" : 45.531569, + "lon" : -122.659045, + "name" : "NE Multnomah & 7th", + "stopCode" : "4054", + "stopId" : "prt:4054", + "stopIndex" : 39, + "stopSequence" : 40, "vertexType" : "TRANSIT", - "zoneId" : "1" + "zoneId" : "0" }, "transitLeg" : true, - "tripBlockId" : "2038", - "tripId" : "prt:200W1230" + "tripBlockId" : "7002", + "tripId" : "prt:700W1170" }, { "agencyId" : "prt:prt", @@ -5155,106 +5199,210 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "agencyUrl" : "http://trimet.org", "arrivalDelay" : 0, "departureDelay" : 0, - "distance" : 981.85, - "endTime" : "2009-11-17T19:22:04.000+00:00", + "distance" : 3838.71, + "endTime" : "2009-11-17T19:08:50.000+00:00", "from" : { - "arrival" : "2009-11-17T19:12:44.000+00:00", - "departure" : "2009-11-17T19:17:51.000+00:00", - "lat" : 45.523512, - "lon" : -122.698081, - "name" : "W Burnside & NW 23rd", - "stopCode" : "755", - "stopId" : "prt:755", - "stopIndex" : 70, - "stopSequence" : 71, + "arrival" : "2009-11-17T18:51:01.000+00:00", + "departure" : "2009-11-17T18:54:29.000+00:00", + "lat" : 45.531569, + "lon" : -122.659045, + "name" : "NE Multnomah & 7th", + "stopCode" : "4054", + "stopId" : "prt:4054", + "stopIndex" : 81, + "stopSequence" : 82, "vertexType" : "TRANSIT", - "zoneId" : "1" + "zoneId" : "0" }, - "generalizedCost" : 1160, - "headsign" : "27th & Thurman", + "generalizedCost" : 1669, + "headsign" : "Montgomery Park", "interlineWithPreviousLeg" : false, "intermediateStops" : [ { - "arrival" : "2009-11-17T19:18:53.000+00:00", - "departure" : "2009-11-17T19:18:53.000+00:00", - "lat" : 45.525416, - "lon" : -122.698381, - "name" : "NW 23rd & Flanders", - "stopCode" : "7157", - "stopId" : "prt:7157", - "stopIndex" : 71, - "stopSequence" : 72, + "arrival" : "2009-11-17T18:55:05.000+00:00", + "departure" : "2009-11-17T18:55:05.000+00:00", + "lat" : 45.531586, + "lon" : -122.660482, + "name" : "NE Multnomah & Grand", + "stopCode" : "4043", + "stopId" : "prt:4043", + "stopIndex" : 82, + "stopSequence" : 83, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:56:09.000+00:00", + "departure" : "2009-11-17T18:56:09.000+00:00", + "lat" : 45.531159, + "lon" : -122.66293, + "name" : "NE Multnomah & 3rd", + "stopCode" : "11492", + "stopId" : "prt:11492", + "stopIndex" : 83, + "stopSequence" : 84, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T18:58:00.000+00:00", + "departure" : "2009-11-17T18:58:00.000+00:00", + "lat" : 45.530005, + "lon" : -122.666476, + "name" : "Rose Quarter Transit Center", + "stopCode" : "2592", + "stopId" : "prt:2592", + "stopIndex" : 84, + "stopSequence" : 85, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T19:01:20.000+00:00", + "departure" : "2009-11-17T19:01:20.000+00:00", + "lat" : 45.526655, + "lon" : -122.676462, + "name" : "NW Glisan & 6th", + "stopCode" : "10803", + "stopId" : "prt:10803", + "stopIndex" : 85, + "stopSequence" : 86, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T19:02:15.000+00:00", + "departure" : "2009-11-17T19:02:15.000+00:00", + "lat" : 45.528799, + "lon" : -122.677238, + "name" : "NW Station Way & Union Station", + "stopCode" : "12801", + "stopId" : "prt:12801", + "stopIndex" : 86, + "stopSequence" : 87, + "vertexType" : "TRANSIT", + "zoneId" : "0" + }, + { + "arrival" : "2009-11-17T19:04:00.000+00:00", + "departure" : "2009-11-17T19:04:00.000+00:00", + "lat" : 45.531582, + "lon" : -122.681193, + "name" : "NW Northrup & 10th", + "stopCode" : "12802", + "stopId" : "prt:12802", + "stopIndex" : 87, + "stopSequence" : 88, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:19:56.000+00:00", - "departure" : "2009-11-17T19:19:56.000+00:00", - "lat" : 45.527543, - "lon" : -122.698473, - "name" : "NW 23rd & Irving", - "stopCode" : "7161", - "stopId" : "prt:7161", - "stopIndex" : 72, - "stopSequence" : 73, + "arrival" : "2009-11-17T19:04:33.000+00:00", + "departure" : "2009-11-17T19:04:33.000+00:00", + "lat" : 45.531534, + "lon" : -122.683319, + "name" : "NW 12th & Northrup", + "stopCode" : "12796", + "stopId" : "prt:12796", + "stopIndex" : 88, + "stopSequence" : 89, "vertexType" : "TRANSIT", "zoneId" : "1" }, { - "arrival" : "2009-11-17T19:21:00.000+00:00", - "departure" : "2009-11-17T19:21:00.000+00:00", - "lat" : 45.529681, - "lon" : -122.698529, - "name" : "NW 23rd & Lovejoy", - "stopCode" : "7163", - "stopId" : "prt:7163", - "stopIndex" : 73, - "stopSequence" : 74, + "arrival" : "2009-11-17T19:05:04.000+00:00", + "departure" : "2009-11-17T19:05:04.000+00:00", + "lat" : 45.531503, + "lon" : -122.685357, + "name" : "NW Northrup & 14th", + "stopCode" : "10775", + "stopId" : "prt:10775", + "stopIndex" : 89, + "stopSequence" : 90, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T19:06:07.000+00:00", + "departure" : "2009-11-17T19:06:07.000+00:00", + "lat" : 45.531434, + "lon" : -122.689417, + "name" : "NW Northrup & 18th", + "stopCode" : "10776", + "stopId" : "prt:10776", + "stopIndex" : 90, + "stopSequence" : 91, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T19:07:24.000+00:00", + "departure" : "2009-11-17T19:07:24.000+00:00", + "lat" : 45.531346, + "lon" : -122.694455, + "name" : "NW Northrup & 21st", + "stopCode" : "10777", + "stopId" : "prt:10777", + "stopIndex" : 91, + "stopSequence" : 92, + "vertexType" : "TRANSIT", + "zoneId" : "1" + }, + { + "arrival" : "2009-11-17T19:07:55.000+00:00", + "departure" : "2009-11-17T19:07:55.000+00:00", + "lat" : 45.531308, + "lon" : -122.696445, + "name" : "NW Northrup & 22nd", + "stopCode" : "10778", + "stopId" : "prt:10778", + "stopIndex" : 92, + "stopSequence" : 93, "vertexType" : "TRANSIT", "zoneId" : "1" } ], "legGeometry" : { - "length" : 22, - "points" : "khztGbn{kVAPkAh@o@?sCB{BD??S?mCDmCDyBB??U?mCDmCDyBB??S?oCDmCDmCBo@@" + "length" : 109, + "points" : "yz{tG`zskV?tBCfD???^?nE?V@Z?PH\\Nb@`@~@Rf@??`@bANb@FV@R?P?pE?jA@h@AnAbBl@LFJN\\f@LT??NXJPPVJFf@Vf@Pp@Nd@NRLB@RNXZR\\vAhC@BhAhD`AhClAbDBrDCnG@n@@^@d@HdAP`CBjEDvD???LqCFmCDYBGDEBGJkAzAQR??KNa@b@MJuBBY?OHW@u@~@aD`EcBhBBrD@xC??@l@BlE@lD???XBjEBpD???VBlE?dA@t@?b@?h@BfEBrD???VBhEFtKDvJ??@\\DnJ???d@FtKmCBo@@" }, "mode" : "BUS", "pathway" : false, "realTime" : false, - "route" : "Belmont/NW 23rd", - "routeId" : "prt:15", - "routeLongName" : "Belmont/NW 23rd", - "routeShortName" : "15", + "route" : "Broadway/Halsey", + "routeId" : "prt:77", + "routeLongName" : "Broadway/Halsey", + "routeShortName" : "77", "routeType" : 3, "serviceDate" : "2009-11-17", - "startTime" : "2009-11-17T19:17:51.000+00:00", + "startTime" : "2009-11-17T18:54:29.000+00:00", "steps" : [ ], "to" : { - "arrival" : "2009-11-17T19:22:04.000+00:00", - "departure" : "2009-11-17T19:22:04.000+00:00", + "arrival" : "2009-11-17T19:08:50.000+00:00", + "departure" : "2009-11-17T19:08:50.000+00:00", "lat" : 45.532159, "lon" : -122.698634, "name" : "NW 23rd & Overton", "stopCode" : "8981", "stopId" : "prt:8981", - "stopIndex" : 74, - "stopSequence" : 75, + "stopIndex" : 93, + "stopSequence" : 94, "vertexType" : "TRANSIT", "zoneId" : "1" }, "transitLeg" : true, - "tripBlockId" : "1550", - "tripId" : "prt:150W1430" + "tripBlockId" : "7702", + "tripId" : "prt:771W1180" }, { "agencyTimeZoneOffset" : -28800000, "arrivalDelay" : 0, "departureDelay" : 0, "distance" : 231.46, - "endTime" : "2009-11-17T19:25:05.000+00:00", + "endTime" : "2009-11-17T19:11:51.000+00:00", "from" : { - "arrival" : "2009-11-17T19:22:04.000+00:00", - "departure" : "2009-11-17T19:22:04.000+00:00", + "arrival" : "2009-11-17T19:08:50.000+00:00", + "departure" : "2009-11-17T19:08:50.000+00:00", "lat" : 45.532159, "lon" : -122.698634, "name" : "NW 23rd & Overton", @@ -5274,7 +5422,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "realTime" : false, "rentedBike" : false, "route" : "", - "startTime" : "2009-11-17T19:22:04.000+00:00", + "startTime" : "2009-11-17T19:08:50.000+00:00", "steps" : [ { "absoluteDirection" : "SOUTH", @@ -5304,7 +5452,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan } ], "to" : { - "arrival" : "2009-11-17T19:25:05.000+00:00", + "arrival" : "2009-11-17T19:11:51.000+00:00", "lat" : 45.531, "lon" : -122.70029, "name" : "NW Northrup St. & NW 24th Ave. (P3)", @@ -5314,14 +5462,14 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "walkingBike" : false } ], - "startTime" : "2009-11-17T18:58:47.000+00:00", + "startTime" : "2009-11-17T18:45:44.000+00:00", "tooSloped" : false, "transfers" : 1, - "transitTime" : 1017, - "waitingTime" : 307, - "walkDistance" : 318.48, + "transitTime" : 1162, + "waitingTime" : 208, + "walkDistance" : 252.2, "walkLimitExceeded" : false, - "walkTime" : 254 + "walkTime" : 197 } ] ] diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java index dacd0dcac9c..8dc4d548c18 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner._support.geometry.Coordinates; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java index 3570d92bdbe..ad31b250046 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java @@ -3,8 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; public class DefaultCostCalculatorTest { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculatorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculatorTest.java index 1aaf3dcd8ef..1cf9a324de5 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculatorTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculatorTest.java @@ -3,7 +3,7 @@ import static graphql.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.raptor._data.transit.TestRoute.route; +import static org.opentripplanner.raptorlegacy._data.transit.TestRoute.route; import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.agency; import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; @@ -16,11 +16,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.framework.model.Cost; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.GeneralizedCostParametersMapper; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunctionTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunctionTest.java index 2769a95b634..b38cefe232c 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunctionTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostLinearFunctionTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.framework.model.Cost; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.test.support.TestTableParser; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculatorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculatorTest.java index b7d3f255a9d..76de708c817 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculatorTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculatorTest.java @@ -6,9 +6,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.api.request.preference.AccessibilityPreferences; import org.opentripplanner.transit.model.basic.Accessibility; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/GeneralizedCostParametersMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/GeneralizedCostParametersMapperTest.java index 9f7458376cc..2620dd506bc 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/GeneralizedCostParametersMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/GeneralizedCostParametersMapperTest.java @@ -10,9 +10,9 @@ import java.util.BitSet; import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.transit.TestRoute; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestRoute; +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.transit.model.framework.FeedScopedId; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java index a8e00056a3f..9424a6b2d13 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java @@ -1,21 +1,28 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.RaptorRequestMapperTest.RequestFeature.RELAX_COST_DEST; +import static org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.RaptorRequestMapperTest.RequestFeature.TRANSIT_GROUP_PRIORITY; +import static org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.RaptorRequestMapperTest.RequestFeature.VIA_PASS_THROUGH; +import static org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.RaptorRequestMapperTest.RequestFeature.VIA_VISIT; import java.time.Duration; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; import java.util.stream.IntStream; +import javax.annotation.Nullable; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor._data.transit.TestAccessEgress; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.request.RaptorRequest; +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.routing.api.request.via.PassThroughViaLocation; @@ -23,11 +30,22 @@ import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.utils.collection.ListUtils; class RaptorRequestMapperTest { private static final TimetableRepositoryForTest TEST_MODEL = TimetableRepositoryForTest.of(); private static final StopLocation STOP_A = TEST_MODEL.stop("Stop:A").build(); + public static final PassThroughViaLocation PASS_THROUGH_VIA_LOCATION = new PassThroughViaLocation( + "Via A", + List.of(STOP_A.getId()) + ); + public static final VisitViaLocation VISIT_VIA_LOCATION = new VisitViaLocation( + "Via A", + null, + List.of(STOP_A.getId()), + List.of() + ); private static final List ACCESS = List.of(TestAccessEgress.walk(12, 45)); private static final List EGRESS = List.of(TestAccessEgress.walk(144, 54)); @@ -36,6 +54,10 @@ class RaptorRequestMapperTest { private static final CostLinearFunction R3 = CostLinearFunction.of("30 + 2.0x"); private static final Map STOPS_MAP = Map.of(STOP_A.getId(), STOP_A); + private static final CostLinearFunction RELAX_TRANSIT_GROUP_PRIORITY = CostLinearFunction.of( + "30m + 1.2t" + ); + public static final double RELAX_GENERALIZED_COST_AT_DESTINATION = 2.0; static List testCasesRelaxedCost() { return List.of( @@ -91,19 +113,114 @@ void testPassThroughPoints() { } @Test - void testPassThroughPointsTurnTransitGroupPriorityOff() { + void testTransitGroupPriority() { var req = new RouteRequest(); - // Set pass-through and relax transit-group-priority - req.setViaLocations(List.of(new PassThroughViaLocation("Via A", List.of(STOP_A.getId())))); + // Set relax transit-group-priority req.withPreferences(p -> p.withTransit(t -> t.withRelaxTransitGroupPriority(CostLinearFunction.of("30m + 1.2t"))) ); var result = map(req); - // transit-group-priority CANNOT be used with pass-through and is turned off... - assertTrue(result.multiCriteria().transitPriorityCalculator().isEmpty()); + assertTrue(result.multiCriteria().transitPriorityCalculator().isPresent()); + } + + static List testViaAndTransitGroupPriorityCombinationsTestCases() { + return List.of( + // If ONE feature is requested, the same feature is expected + Arguments.of( + "VIA_PASS_THROUGH only", + List.of(VIA_PASS_THROUGH), + List.of(VIA_PASS_THROUGH), + null + ), + Arguments.of("VIA_VISIT only", List.of(VIA_VISIT), List.of(VIA_VISIT), null), + Arguments.of( + "TRANSIT_GROUP_PRIORITY only", + List.of(TRANSIT_GROUP_PRIORITY), + List.of(TRANSIT_GROUP_PRIORITY), + null + ), + Arguments.of( + "RELAX_COST_DEST only", + List.of(RELAX_COST_DEST), + List.of(RELAX_COST_DEST), + null + ), + Arguments.of( + "VIA_VISIT is not allowed together VIA_PASS_THROUGH, an error is expected.", + List.of(VIA_VISIT, VIA_PASS_THROUGH), + List.of(), + "A mix of via-locations and pass-through is not allowed in this version." + ), + Arguments.of( + """ + VIA_VISIT is not allowed together VIA_PASS_THROUGH, an error is expected. + Other features are ignored. + """, + List.of(VIA_VISIT, VIA_PASS_THROUGH, TRANSIT_GROUP_PRIORITY, RELAX_COST_DEST), + List.of(), + "A mix of via-locations and pass-through is not allowed in this version." + ), + Arguments.of( + "VIA_PASS_THROUGH cannot be combined with other features, and other features are dropped", + List.of(VIA_PASS_THROUGH, TRANSIT_GROUP_PRIORITY, RELAX_COST_DEST), + List.of(VIA_PASS_THROUGH), + null + ), + Arguments.of( + "VIA_VISIT can be combined with TRANSIT_GROUP_PRIORITY", + List.of(VIA_VISIT, TRANSIT_GROUP_PRIORITY), + List.of(VIA_VISIT, TRANSIT_GROUP_PRIORITY), + null + ), + Arguments.of( + """ + VIA_VISIT can only be combined with TRANSIT_GROUP_PRIORITY, and other features are dropped + VIA_PASS_THROUGH override VIA_VISIT (see above) + """, + List.of(VIA_VISIT, TRANSIT_GROUP_PRIORITY, RELAX_COST_DEST), + List.of(VIA_VISIT, TRANSIT_GROUP_PRIORITY), + null + ), + Arguments.of( + """ + TRANSIT_GROUP_PRIORITY cannot be combined with other features, override RELAX_COST_DEST + VIA_PASS_THROUGH and VIA_VISIT override VIA_VISIT (see above) + """, + List.of(TRANSIT_GROUP_PRIORITY, RELAX_COST_DEST), + List.of(TRANSIT_GROUP_PRIORITY), + null + ) + ); + } + + @ParameterizedTest(name = "{0}. {1} => {2}") + @MethodSource("testViaAndTransitGroupPriorityCombinationsTestCases") + void testViaAndTransitGroupPriorityCombinations( + String testDescription, + List requestedFeatures, + List expectedFeatures, + @Nullable String errorMessage + ) { + var req = new RouteRequest(); + + for (RequestFeature it : requestedFeatures) { + req = setFeaturesOnRequest(req, it); + } + + if (errorMessage == null) { + var result = map(req); + + for (var feature : RequestFeature.values()) { + assertFeatureSet(feature, result, expectedFeatures.contains(feature)); + } + } else { + var r = req; + var ex = Assertions.assertThrows(IllegalArgumentException.class, () -> map(r)); + assertEquals(errorMessage, ex.getMessage()); + } } private static RaptorRequest map(RouteRequest request) { @@ -117,4 +234,73 @@ private static RaptorRequest map(RouteRequest request) { id -> IntStream.of(STOPS_MAP.get(id).getIndex()) ); } + + private static void assertFeatureSet( + RequestFeature feature, + RaptorRequest result, + boolean expected + ) { + switch (feature) { + case VIA_VISIT: + if (expected) { + assertTrue(result.searchParams().hasViaLocations()); + // One via location exist(no NPE), but it does not allow pass-through + assertEquals( + "Via{label: Via A, connections: [0]}", + result.searchParams().viaLocations().get(0).toString() + ); + } + break; + case VIA_PASS_THROUGH: + if (expected) { + assertTrue(result.multiCriteria().hasPassThroughPoints()); + assertEquals( + "(Via A, stops: 0)", + result.multiCriteria().passThroughPoints().get(0).toString() + ); + } + break; + case TRANSIT_GROUP_PRIORITY: + assertEquals(expected, result.multiCriteria().transitPriorityCalculator().isPresent()); + if (expected) { + assertFalse(result.multiCriteria().hasPassThroughPoints()); + } + break; + case RELAX_COST_DEST: + assertEquals(expected, result.multiCriteria().relaxCostAtDestination() != null); + if (expected) { + assertFalse(result.multiCriteria().hasPassThroughPoints()); + assertFalse(result.searchParams().hasViaLocations()); + } + break; + } + } + + private static RouteRequest setFeaturesOnRequest(RouteRequest req, RequestFeature feature) { + return switch (feature) { + case VIA_VISIT -> req.setViaLocations( + ListUtils.combine(req.getViaLocations(), List.of(VISIT_VIA_LOCATION)) + ); + case VIA_PASS_THROUGH -> req.setViaLocations( + ListUtils.combine(req.getViaLocations(), List.of(PASS_THROUGH_VIA_LOCATION)) + ); + case TRANSIT_GROUP_PRIORITY -> req.withPreferences(p -> + p.withTransit(t -> t.withRelaxTransitGroupPriority(RELAX_TRANSIT_GROUP_PRIORITY)) + ); + case RELAX_COST_DEST -> req.withPreferences(p -> + p.withTransit(t -> + t.withRaptor(r -> + r.withRelaxGeneralizedCostAtDestination(RELAX_GENERALIZED_COST_AT_DESTINATION) + ) + ) + ); + }; + } + + enum RequestFeature { + VIA_VISIT, + VIA_PASS_THROUGH, + TRANSIT_GROUP_PRIORITY, + RELAX_COST_DEST, + } } diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java index 2b0751c7456..9985c3e559f 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.opentripplanner.model.Timetable; -import org.opentripplanner.model.plan.PlanTestConstants; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripPatternForDate; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -38,7 +37,7 @@ public static void setUp() throws Exception { var trip = TimetableRepositoryForTest.trip("1").build(); var tripTimes = TripTimesFactory.tripTimes( trip, - TEST_MODEL.stopTimesEvery5Minutes(5, trip, PlanTestConstants.T11_00), + TEST_MODEL.stopTimesEvery5Minutes(5, trip, "11:00"), new Deduplicator() ); tripTimes.setServiceCode(SERVICE_CODE); diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripAssert.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripAssert.java index fa76a2790ed..37620d80bc3 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripAssert.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripAssert.java @@ -4,9 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; class TripAssert { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearchTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearchTest.java index 8fddca9511f..55cb99b2f82 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearchTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearchTest.java @@ -1,18 +1,18 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptorlegacy._data.transit.TestTripPattern.pattern; +import static org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule.schedule; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestRoute; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.SearchDirection; import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestRoute; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; public class TripScheduleAlightSearchTest implements RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearchTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearchTest.java index dd3a7d64cf6..28811fcc42c 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearchTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearchTest.java @@ -1,17 +1,17 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule.schedule; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestRoute; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.SearchDirection; import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestRoute; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; public class TripScheduleBoardSearchTest implements RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/api/OptimizedPathTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/api/OptimizedPathTest.java index 788192c45b5..bc01a082740 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/api/OptimizedPathTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/api/OptimizedPathTest.java @@ -4,9 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase; import org.opentripplanner.raptor.api.model.RaptorValueFormatter; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase; class OptimizedPathTest implements RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/MinSafeTransferTimeCalculatorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/MinSafeTransferTimeCalculatorTest.java index 9b0cf342c0b..d5d8f7c7c62 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/MinSafeTransferTimeCalculatorTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/MinSafeTransferTimeCalculatorTest.java @@ -1,17 +1,17 @@ package org.opentripplanner.routing.algorithm.transferoptimization.model; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; +import static org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; import static org.opentripplanner.routing.algorithm.transferoptimization.model.MinSafeTransferTimeCalculator.bound; import static org.opentripplanner.utils.time.TimeUtils.time; import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.api.TestPathBuilder; -import org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.api.TestPathBuilder; +import org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.utils.time.DurationUtils; public class MinSafeTransferTimeCalculatorTest implements RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java index 8c82f68a8bc..505b6581000 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java @@ -4,11 +4,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; import org.opentripplanner.raptor.api.path.TransitPathLeg; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.transferoptimization.services.TestTransferBuilder; class OptimizedPathTailTest implements RaptorTestConstants { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TripStopTimeTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TripStopTimeTest.java index e81b51c4eaa..640dd48d27d 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TripStopTimeTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/TripStopTimeTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.utils.time.TimeUtils.time; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.transit.TestTripPattern; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.utils.time.TimeUtils; public class TripStopTimeTest { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/costfilter/MinCostPathTailFilterTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/costfilter/MinCostPathTailFilterTest.java index 8ecf0006e48..e32559c2dad 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/costfilter/MinCostPathTailFilterTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/costfilter/MinCostPathTailFilterTest.java @@ -1,15 +1,15 @@ package org.opentripplanner.routing.algorithm.transferoptimization.model.costfilter; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; +import static org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.transferoptimization.model.OptimizedPathTail; import org.opentripplanner.routing.algorithm.transferoptimization.model.TransferWaitTimeCostCalculator; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughNoTransfersTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughNoTransfersTest.java index 11552ea3d1e..ff1f37ef5fa 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughNoTransfersTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughNoTransfersTest.java @@ -10,8 +10,8 @@ import java.util.List; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; /** * This test focus on the PASS-THROUGH functionality with a very simple scenario - one transit leg diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughOneTransferTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughOneTransferTest.java index acd99ab4bef..d1bc95f3b68 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughOneTransferTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughOneTransferTest.java @@ -12,8 +12,8 @@ import java.util.stream.Collectors; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; /** * This test focus on the PASS-THROUGH functionality with two transit legs and one transfer in the diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughTwoTransfersTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughTwoTransfersTest.java index 05da490c857..bed2bff0202 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughTwoTransfersTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/PassThroughTwoTransfersTest.java @@ -10,10 +10,10 @@ import java.util.List; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.api.PathUtils; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.api.PathUtils; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; /** * This test focus on the PASS-THROUGH functionality with three transit legs and two transfer in diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestCase.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestCase.java index 3b2059cf4d9..8ca961dcab0 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestCase.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestCase.java @@ -1,8 +1,8 @@ package org.opentripplanner.routing.algorithm.transferoptimization.model.passthrough; import java.util.List; -import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor.api.request.PassThroughPoint; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; record TestCase( String description, diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestUtils.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestUtils.java index 2faf0ee95c1..103e2c88f3f 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestUtils.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/passthrough/TestUtils.java @@ -4,12 +4,12 @@ import java.util.Collection; import java.util.List; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.api.TestPathBuilder; -import org.opentripplanner.raptor._data.transit.TestTransitData; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.request.PassThroughPoint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.api.TestPathBuilder; +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; import org.opentripplanner.routing.algorithm.transferoptimization.model.TripToTripTransfer; import org.opentripplanner.routing.algorithm.transferoptimization.model.costfilter.MinCostPathTailFilterFactory; diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceConstrainedTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceConstrainedTest.java index 109032a0110..3cfba2330c1 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceConstrainedTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceConstrainedTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferPriority; -import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.api.PathUtils; -import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants; +import org.opentripplanner.raptorlegacy._data.api.PathUtils; +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule; /** *

    diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceTest.java
    index 54c45b8a76b..6a023138751 100644
    --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceTest.java
    +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainServiceTest.java
    @@ -8,13 +8,13 @@
     import java.util.List;
     import javax.annotation.Nullable;
     import org.junit.jupiter.api.Test;
    -import org.opentripplanner.raptor._data.RaptorTestConstants;
    -import org.opentripplanner.raptor._data.api.PathUtils;
    -import org.opentripplanner.raptor._data.api.TestPathBuilder;
    -import org.opentripplanner.raptor._data.transit.TestTripSchedule;
     import org.opentripplanner.raptor.spi.DefaultSlackProvider;
     import org.opentripplanner.raptor.spi.RaptorCostCalculator;
     import org.opentripplanner.raptor.spi.RaptorSlackProvider;
    +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants;
    +import org.opentripplanner.raptorlegacy._data.api.PathUtils;
    +import org.opentripplanner.raptorlegacy._data.api.TestPathBuilder;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule;
     import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.TransferWaitTimeCostCalculator;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.costfilter.MinCostPathTailFilterFactory;
    diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TestTransferBuilder.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TestTransferBuilder.java
    index d553bac75eb..f32b5366907 100644
    --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TestTransferBuilder.java
    +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TestTransferBuilder.java
    @@ -5,9 +5,9 @@
     import org.opentripplanner.model.transfer.TransferConstraint;
     import org.opentripplanner.model.transfer.TransferPriority;
     import org.opentripplanner.model.transfer.TripTransferPoint;
    -import org.opentripplanner.raptor._data.transit.TestTransfer;
     import org.opentripplanner.raptor.api.model.RaptorConstants;
     import org.opentripplanner.raptor.api.model.RaptorTripSchedule;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.TripStopTime;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.TripToTripTransfer;
     import org.opentripplanner.transit.model._data.TimetableRepositoryForTest;
    diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorDummy.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorDummy.java
    index 4392e89a83d..e021a38a8de 100644
    --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorDummy.java
    +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorDummy.java
    @@ -2,9 +2,9 @@
     
     import java.util.Arrays;
     import java.util.List;
    -import org.opentripplanner.raptor._data.transit.TestTransitData;
    -import org.opentripplanner.raptor._data.transit.TestTripSchedule;
     import org.opentripplanner.raptor.api.path.TransitPathLeg;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.TripToTripTransfer;
     
     /**
    diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorTest.java
    index acaefbcf1d9..4e1d89c6b49 100644
    --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorTest.java
    +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGeneratorTest.java
    @@ -2,9 +2,9 @@
     
     import static java.time.Duration.ofMinutes;
     import static org.junit.jupiter.api.Assertions.assertEquals;
    -import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.C1_CALCULATOR;
    -import static org.opentripplanner.raptor._data.transit.TestRoute.route;
    -import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule;
    +import static org.opentripplanner.raptorlegacy._data.stoparrival.BasicPathTestCase.C1_CALCULATOR;
    +import static org.opentripplanner.raptorlegacy._data.transit.TestRoute.route;
    +import static org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule.schedule;
     
     import java.time.Duration;
     import java.util.List;
    @@ -17,17 +17,17 @@
     import org.junit.jupiter.params.provider.Arguments;
     import org.junit.jupiter.params.provider.MethodSource;
     import org.opentripplanner.model.transfer.TransferConstraint;
    -import org.opentripplanner.raptor._data.RaptorTestConstants;
    -import org.opentripplanner.raptor._data.api.TestPathBuilder;
    -import org.opentripplanner.raptor._data.transit.TestRoute;
    -import org.opentripplanner.raptor._data.transit.TestTransfer;
    -import org.opentripplanner.raptor._data.transit.TestTransitData;
    -import org.opentripplanner.raptor._data.transit.TestTripPattern;
    -import org.opentripplanner.raptor._data.transit.TestTripSchedule;
     import org.opentripplanner.raptor.api.path.RaptorPath;
     import org.opentripplanner.raptor.api.path.TransitPathLeg;
     import org.opentripplanner.raptor.spi.DefaultSlackProvider;
     import org.opentripplanner.raptor.spi.RaptorSlackProvider;
    +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants;
    +import org.opentripplanner.raptorlegacy._data.api.TestPathBuilder;
    +import org.opentripplanner.raptorlegacy._data.transit.TestRoute;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTransfer;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTransitData;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTripPattern;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule;
     import org.opentripplanner.utils.time.TimeUtils;
     
     public class TransferGeneratorTest implements RaptorTestConstants {
    diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransitPathLegSelectorTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransitPathLegSelectorTest.java
    index 5ce84b4a00d..5ca18f63cb9 100644
    --- a/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransitPathLegSelectorTest.java
    +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransitPathLegSelectorTest.java
    @@ -8,14 +8,14 @@
     import java.util.Set;
     import java.util.stream.Collectors;
     import org.junit.jupiter.api.Test;
    -import org.opentripplanner.raptor._data.RaptorTestConstants;
    -import org.opentripplanner.raptor._data.transit.TestAccessEgress;
    -import org.opentripplanner.raptor._data.transit.TestTripSchedule;
     import org.opentripplanner.raptor.api.path.EgressPathLeg;
     import org.opentripplanner.raptor.api.path.TransitPathLeg;
     import org.opentripplanner.raptor.spi.DefaultSlackProvider;
     import org.opentripplanner.raptor.spi.RaptorCostCalculator;
     import org.opentripplanner.raptor.spi.RaptorSlackProvider;
    +import org.opentripplanner.raptorlegacy._data.RaptorTestConstants;
    +import org.opentripplanner.raptorlegacy._data.transit.TestAccessEgress;
    +import org.opentripplanner.raptorlegacy._data.transit.TestTripSchedule;
     import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.OptimizedPathTail;
     import org.opentripplanner.routing.algorithm.transferoptimization.model.PathTailFilter;
    diff --git a/application/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java b/application/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java
    index b77a7d9085b..bcdfcfc545c 100644
    --- a/application/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java
    +++ b/application/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java
    @@ -81,6 +81,12 @@ void relaxTransitGroupPriority() {
         assertEquals(TRANSIT_GROUP_PRIORITY_RELAX, subject.relaxTransitGroupPriority());
       }
     
    +  @Test
    +  void isRelaxTransitGroupPrioritySet() {
    +    assertTrue(subject.isRelaxTransitGroupPrioritySet());
    +    assertFalse(TransitPreferences.DEFAULT.isRelaxTransitGroupPrioritySet());
    +  }
    +
       @Test
       void ignoreRealtimeUpdates() {
         assertFalse(TransitPreferences.DEFAULT.ignoreRealtimeUpdates());
    diff --git a/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java b/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java
    index 055fba784a9..65e99825a9e 100644
    --- a/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java
    +++ b/application/src/test/java/org/opentripplanner/routing/stoptimes/AlternativeLegsTest.java
    @@ -12,6 +12,7 @@
     import org.opentripplanner.model.plan.legreference.ScheduledTransitLegReference;
     import org.opentripplanner.routing.alternativelegs.AlternativeLegs;
     import org.opentripplanner.routing.alternativelegs.AlternativeLegsFilter;
    +import org.opentripplanner.routing.alternativelegs.NavigationDirection;
     import org.opentripplanner.transit.model.framework.FeedScopedId;
     import org.opentripplanner.transit.service.DefaultTransitService;
     
    @@ -51,7 +52,7 @@ void testPreviousLegs() {
           originalLeg,
           3,
           transitService,
    -      true,
    +      NavigationDirection.PREVIOUS,
           AlternativeLegsFilter.NO_FILTER
         );
     
    @@ -85,7 +86,7 @@ void testNextLegs() {
           originalLeg,
           3,
           transitService,
    -      false,
    +      NavigationDirection.NEXT,
           AlternativeLegsFilter.NO_FILTER
         );
     
    @@ -119,7 +120,7 @@ void testCircularRoutes() {
           originalLeg,
           2,
           transitService,
    -      false,
    +      NavigationDirection.NEXT,
           AlternativeLegsFilter.NO_FILTER
         );
     
    @@ -147,7 +148,7 @@ void testComplexCircularRoutes() {
           originalLeg,
           2,
           transitService,
    -      false,
    +      NavigationDirection.NEXT,
           AlternativeLegsFilter.NO_FILTER
         );
         var legs = toString(alternativeLegs);
    diff --git a/application/src/test/java/org/opentripplanner/standalone/config/CommandLineParametersTest.java b/application/src/test/java/org/opentripplanner/standalone/config/CommandLineParametersTest.java
    index 82c3589202c..0551d9e7056 100644
    --- a/application/src/test/java/org/opentripplanner/standalone/config/CommandLineParametersTest.java
    +++ b/application/src/test/java/org/opentripplanner/standalone/config/CommandLineParametersTest.java
    @@ -41,6 +41,8 @@ public void everyThingOffByDefault() {
         assertFalse(subject.doLoadStreetGraph());
         assertFalse(subject.doSaveGraph());
         assertFalse(subject.doServe());
    +    var ex = assertThrows(ParameterException.class, () -> subject.inferAndValidate());
    +    assertEquals("Nothing to do. Use --help to see available options.", ex.getMessage());
       }
     
       @Test
    @@ -50,6 +52,7 @@ public void build() {
         assertTrue(subject.doBuildTransit());
         assertFalse(subject.doSaveGraph());
         assertFalse(subject.doServe());
    +    assertEquals("Build Street & Transit Graph", subject.logTaskInfo());
     
         subject.save = true;
         subject.serve = false;
    @@ -58,6 +61,7 @@ public void build() {
         assertTrue(subject.doSaveGraph());
         assertFalse(subject.doServe());
         subject.inferAndValidate();
    +    assertEquals("Build Street & Transit Graph", subject.logTaskInfo());
     
         subject.save = false;
         subject.serve = true;
    @@ -66,6 +70,7 @@ public void build() {
         assertFalse(subject.doSaveGraph());
         assertTrue(subject.doServe());
         subject.inferAndValidate();
    +    assertEquals("Build Street & Transit Graph, Run Server", subject.logTaskInfo());
     
         subject.save = true;
         subject.serve = true;
    @@ -74,6 +79,7 @@ public void build() {
         assertTrue(subject.doSaveGraph());
         assertTrue(subject.doServe());
         subject.inferAndValidate();
    +    assertEquals("Build Street & Transit Graph, Run Server", subject.logTaskInfo());
       }
     
       @Test
    @@ -83,6 +89,7 @@ public void buildStreet() {
         assertFalse(subject.doBuildTransit());
         assertTrue(subject.doSaveStreetGraph());
         assertFalse(subject.doSaveGraph());
    +    assertEquals("Build Street Graph", subject.logTaskInfo());
       }
     
       @Test
    @@ -90,6 +97,7 @@ public void doLoadGraph() {
         subject.load = true;
         assertTrue(subject.doLoadGraph());
         assertTrue(subject.doServe());
    +    assertEquals("Run Server", subject.logTaskInfo());
       }
     
       @Test
    @@ -99,6 +107,7 @@ public void doLoadStreetGraph() {
         assertFalse(subject.doBuildStreet());
         assertFalse(subject.doSaveStreetGraph());
         assertFalse(subject.doSaveGraph());
    +    assertEquals("Build Transit Graph", subject.logTaskInfo());
     
         subject.save = true;
         subject.serve = true;
    @@ -119,6 +128,7 @@ public void validateLoad() {
     
         // Implicit given, but should be ok to set
         subject.serve = true;
    +    assertEquals("Run Server", subject.logTaskInfo());
     
         // No exception thrown
         subject.inferAndValidate();
    diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java
    index 3375d94f5af..0a9dfa44440 100644
    --- a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java
    +++ b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java
    @@ -13,6 +13,8 @@
     import static org.opentripplanner.street.search.state.VehicleRentalState.HAVE_RENTED;
     import static org.opentripplanner.street.search.state.VehicleRentalState.RENTING_FLOATING;
     
    +import java.util.Arrays;
    +import java.util.Collections;
     import java.util.Set;
     import org.junit.jupiter.api.Nested;
     import org.junit.jupiter.api.Test;
    @@ -213,14 +215,7 @@ public void backwardsDontEnterNoTraversalZone() {
         public void pickupFloatingVehicleWhenLeavingAZone() {
           var req = defaultArriveByRequest();
     
    -      // this is the state that starts inside a restricted zone (no drop off, no traversal or outside business area)
    -      // and is walking towards finding a rental vehicle
    -      var haveRentedState = State
    -        .getInitialStates(Set.of(V2), req)
    -        .stream()
    -        .filter(s -> s.getVehicleRentalState() == HAVE_RENTED)
    -        .findAny()
    -        .get();
    +      var haveRentedState = makeHaveRentedState(V2, req);
     
           var edge = streetEdge(V1, V2);
           V2.addRentalRestriction(NO_TRAVERSAL);
    @@ -244,25 +239,23 @@ public void pickupFloatingVehiclesWhenStartedInNoDropOffZone() {
           V2.addRentalRestriction(NO_DROP_OFF_TIER);
           V2.addRentalRestriction(noDropOffRestriction(NETWORK_BIRD));
     
    -      // this is the state that starts inside a no drop off
    -      // and is walking towards finding a rental vehicle
    -      var haveRentedState = State
    -        .getInitialStates(Set.of(V2), req)
    -        .stream()
    -        .filter(s -> s.getVehicleRentalState() == HAVE_RENTED)
    -        .findAny()
    -        .get();
    +      var haveRentedState = makeHaveRentedState(V2, req);
     
           var edge = streetEdge(V1, V2);
     
           var states = edge.traverse(haveRentedState);
     
    -      // we return 3 states: one for the speculative renting of a vehicle, but with the information
    -      // of which networks' no-drop-off zones it started in
    +      // we return 3 states: one for continuing walking, one for the speculative renting of
    +      // a vehicle, but with the information of which networks' no-drop-off zones it started in
           assertEquals(3, states.length);
     
    -      // first the speculative renting case
    -      final State speculativeRenting = states[0];
    +      // first the fallback walk state
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +
    +      // then the speculative renting case for unknown rental network
    +      final State speculativeRenting = states[2];
           assertEquals(RENTING_FLOATING, speculativeRenting.getVehicleRentalState());
           assertEquals(BICYCLE, speculativeRenting.currentMode());
           // null means that the vehicle has been rented speculatively and the rest of the backwards search
    @@ -273,23 +266,182 @@ public void pickupFloatingVehiclesWhenStartedInNoDropOffZone() {
             speculativeRenting.stateData.noRentalDropOffZonesAtStartOfReverseSearch
           );
     
    -      // first the speculative renting case
    +      // then the speculative renting cases for specific rental networks
           final State tierState = states[1];
           assertEquals(RENTING_FLOATING, tierState.getVehicleRentalState());
           assertEquals(BICYCLE, tierState.currentMode());
           assertEquals(NETWORK_TIER, tierState.getVehicleRentalNetwork());
           assertEquals(Set.of(), tierState.stateData.noRentalDropOffZonesAtStartOfReverseSearch);
    +    }
    +
    +    @Test
    +    public void pickupFloatingVehiclesWhenStartedInNoDropOffZoneAllNetworksAllowedByDefault() {
    +      var states = runTraverse(Collections.emptySet(), Collections.emptySet());
    +
    +      // Walking, unknown, Tier, Bird. (Order of last two not guaranteed)
    +      assertEquals(4, states.length);
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +
    +      final State unknownNetworkState = states[3];
    +      assertEquals(RENTING_FLOATING, unknownNetworkState.getVehicleRentalState());
    +      assertEquals(BICYCLE, unknownNetworkState.currentMode());
    +      assertNull(unknownNetworkState.getVehicleRentalNetwork());
    +
    +      final State tierState = Arrays
    +        .stream(states)
    +        .filter(s -> NETWORK_TIER.equals(s.getVehicleRentalNetwork()))
    +        .findFirst()
    +        .get();
    +      assertEquals(RENTING_FLOATING, tierState.getVehicleRentalState());
    +      assertEquals(BICYCLE, tierState.currentMode());
    +
    +      final State birdState = Arrays
    +        .stream(states)
    +        .filter(s -> NETWORK_BIRD.equals(s.getVehicleRentalNetwork()))
    +        .findFirst()
    +        .get();
    +      assertEquals(RENTING_FLOATING, birdState.getVehicleRentalState());
    +      assertEquals(BICYCLE, birdState.currentMode());
    +    }
    +
    +    @Test
    +    public void pickupFloatingVehiclesWhenStartedInNoDropOffZoneAllNetworksAllowed() {
    +      var states = runTraverse(Set.of(NETWORK_TIER, NETWORK_BIRD), Collections.emptySet());
    +
    +      // Walking, unknown, Tier, Bird. (Order of last two not guaranteed)
    +      assertEquals(4, states.length);
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +
    +      final State unknownNetworkState = states[3];
    +      assertEquals(RENTING_FLOATING, unknownNetworkState.getVehicleRentalState());
    +      assertEquals(BICYCLE, unknownNetworkState.currentMode());
    +      assertNull(unknownNetworkState.getVehicleRentalNetwork());
    +
    +      final State tierState = Arrays
    +        .stream(states)
    +        .filter(s -> NETWORK_TIER.equals(s.getVehicleRentalNetwork()))
    +        .findFirst()
    +        .get();
    +      assertEquals(RENTING_FLOATING, tierState.getVehicleRentalState());
    +      assertEquals(BICYCLE, tierState.currentMode());
     
    -      final State birdState = states[2];
    +      final State birdState = Arrays
    +        .stream(states)
    +        .filter(s -> NETWORK_BIRD.equals(s.getVehicleRentalNetwork()))
    +        .findFirst()
    +        .get();
           assertEquals(RENTING_FLOATING, birdState.getVehicleRentalState());
           assertEquals(BICYCLE, birdState.currentMode());
    -      assertEquals(NETWORK_BIRD, birdState.getVehicleRentalNetwork());
    -      assertEquals(Set.of(), birdState.stateData.noRentalDropOffZonesAtStartOfReverseSearch);
    +    }
    +
    +    @Test
    +    public void pickupFloatingVehiclesWhenStartedInNoDropOffZoneSomeNetworksAllowed() {
    +      var states = runTraverse(Set.of(NETWORK_TIER), Collections.emptySet());
    +
    +      // Walking, unknown, Tier.
    +      assertEquals(3, states.length);
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +
    +      final State unknownNetworkState = states[2];
    +      assertEquals(RENTING_FLOATING, unknownNetworkState.getVehicleRentalState());
    +      assertEquals(BICYCLE, unknownNetworkState.currentMode());
    +      assertNull(unknownNetworkState.getVehicleRentalNetwork());
    +
    +      final State tierState = states[1];
    +      assertEquals(RENTING_FLOATING, tierState.getVehicleRentalState());
    +      assertEquals(BICYCLE, tierState.currentMode());
    +      assertEquals(NETWORK_TIER, tierState.getVehicleRentalNetwork());
    +    }
    +
    +    @Test
    +    public void pickupFloatingVehiclesWhenStartedInNoDropOffZoneAllNetworksBanned() {
    +      var states = runTraverse(Collections.emptySet(), Set.of(NETWORK_TIER, NETWORK_BIRD));
    +
    +      // Should only have a walking state. The unknown network state should only be
    +      // generated if there are known network states.
    +      assertEquals(1, states.length);
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +    }
    +
    +    @Test
    +    public void pickupFloatingVehiclesWhenStartedInNoDropOffZoneSomeNetworksBanned() {
    +      var states = runTraverse(Collections.emptySet(), Set.of(NETWORK_BIRD));
    +
    +      // Walking state, unknown network state, known network state for Tier (which wasn't banned)
    +      assertEquals(3, states.length);
    +
    +      final State walkState = states[0];
    +      assertEquals(HAVE_RENTED, walkState.getVehicleRentalState());
    +      assertEquals(WALK, walkState.currentMode());
    +
    +      final State unknownNetworkState = states[2];
    +      assertEquals(RENTING_FLOATING, unknownNetworkState.getVehicleRentalState());
    +      assertEquals(BICYCLE, unknownNetworkState.currentMode());
    +      assertNull(unknownNetworkState.getVehicleRentalNetwork());
    +
    +      final State tierState = states[1];
    +      assertEquals(RENTING_FLOATING, tierState.getVehicleRentalState());
    +      assertEquals(BICYCLE, tierState.currentMode());
    +      assertEquals(NETWORK_TIER, tierState.getVehicleRentalNetwork());
    +    }
    +
    +    private State[] runTraverse(Set allowedNetworks, Set bannedNetworks) {
    +      var req = makeArriveByRequest(allowedNetworks, bannedNetworks);
    +
    +      V2.addRentalRestriction(NO_DROP_OFF_TIER);
    +      V2.addRentalRestriction(noDropOffRestriction(NETWORK_BIRD));
    +
    +      var haveRentedState = makeHaveRentedState(V2, req);
    +
    +      var edge = streetEdge(V1, V2);
    +
    +      return edge.traverse(haveRentedState);
    +    }
    +
    +    private static State makeHaveRentedState(Vertex vertex, StreetSearchRequest req) {
    +      // this is the state that starts inside a restricted zone
    +      // (no drop off, no traversal or outside business area)
    +      // and is walking towards finding a rental vehicle
    +      return State
    +        .getInitialStates(Set.of(vertex), req)
    +        .stream()
    +        .filter(s -> s.getVehicleRentalState() == HAVE_RENTED)
    +        .findAny()
    +        .get();
         }
     
         private static StreetSearchRequest defaultArriveByRequest() {
           return StreetSearchRequest
             .of()
    +        .withPreferences(p ->
    +          p.withBike(b -> b.withRental(r -> r.withAllowedNetworks(Set.of(NETWORK_TIER))))
    +        )
    +        .withMode(StreetMode.SCOOTER_RENTAL)
    +        .withArriveBy(true)
    +        .build();
    +    }
    +
    +    private static StreetSearchRequest makeArriveByRequest(
    +      Set allowedNetworks,
    +      Set bannedNetworks
    +    ) {
    +      return StreetSearchRequest
    +        .of()
    +        .withPreferences(p ->
    +          p.withBike(b ->
    +            b.withRental(r ->
    +              r.withAllowedNetworks(allowedNetworks).withBannedNetworks(bannedNetworks)
    +            )
    +          )
    +        )
             .withMode(StreetMode.SCOOTER_RENTAL)
             .withArriveBy(true)
             .build();
    diff --git a/application/src/test/java/org/opentripplanner/street/search/state/EdgeTraverserTest.java b/application/src/test/java/org/opentripplanner/street/search/state/EdgeTraverserTest.java
    new file mode 100644
    index 00000000000..a2cd7e61b62
    --- /dev/null
    +++ b/application/src/test/java/org/opentripplanner/street/search/state/EdgeTraverserTest.java
    @@ -0,0 +1,191 @@
    +package org.opentripplanner.street.search.state;
    +
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertSame;
    +import static org.junit.jupiter.api.Assertions.assertTrue;
    +import static org.opentripplanner.street.model._data.StreetModelForTest.intersectionVertex;
    +
    +import java.time.Instant;
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.Set;
    +import java.util.function.Function;
    +import org.junit.jupiter.api.Test;
    +import org.opentripplanner._support.geometry.Coordinates;
    +import org.opentripplanner.routing.api.request.StreetMode;
    +import org.opentripplanner.street.model.StreetTraversalPermission;
    +import org.opentripplanner.street.model._data.StreetModelForTest;
    +import org.opentripplanner.street.model.edge.Edge;
    +import org.opentripplanner.street.model.vertex.IntersectionVertex;
    +import org.opentripplanner.street.search.TraverseMode;
    +import org.opentripplanner.street.search.request.StreetSearchRequest;
    +
    +class EdgeTraverserTest {
    +
    +  private static final IntersectionVertex BERLIN_V = intersectionVertex(Coordinates.BERLIN);
    +  private static final IntersectionVertex BRANDENBURG_GATE_V = intersectionVertex(
    +    Coordinates.BERLIN_BRANDENBURG_GATE
    +  );
    +  private static final IntersectionVertex FERNSEHTURM_V = intersectionVertex(
    +    Coordinates.BERLIN_FERNSEHTURM
    +  );
    +  private static final IntersectionVertex ADMIRALBRUCKE_V = intersectionVertex(
    +    Coordinates.BERLIN_ADMIRALBRUCKE
    +  );
    +
    +  @Test
    +  void emptyEdges() {
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.WALK)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(BERLIN_V), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, List.of());
    +
    +    assertSame(initialStates.iterator().next(), traversedState.get());
    +  }
    +
    +  @Test
    +  void failedTraversal() {
    +    var edge = StreetModelForTest
    +      .streetEdge(BERLIN_V, BRANDENBURG_GATE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.NONE)
    +      .buildAndConnect();
    +
    +    var edges = List.of(edge);
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.WALK)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(edge.getFromVertex()), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, edges);
    +
    +    assertTrue(traversedState.isEmpty());
    +  }
    +
    +  @Test
    +  void withSingleState() {
    +    var edge = StreetModelForTest
    +      .streetEdge(BERLIN_V, BRANDENBURG_GATE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.ALL)
    +      .buildAndConnect();
    +
    +    var edges = List.of(edge);
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.WALK)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(edge.getFromVertex()), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, edges).get();
    +
    +    assertEquals(List.of(TraverseMode.WALK), stateValues(traversedState, State::getBackMode));
    +    assertEquals(1719, traversedState.getElapsedTimeSeconds());
    +  }
    +
    +  @Test
    +  void withSingleArriveByState() {
    +    var edge = StreetModelForTest
    +      .streetEdge(BERLIN_V, BRANDENBURG_GATE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.ALL)
    +      .buildAndConnect();
    +
    +    var edges = List.of(edge);
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.WALK)
    +      .withArriveBy(true)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(edge.getToVertex()), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, edges).get();
    +
    +    assertSame(BERLIN_V, traversedState.getVertex());
    +    assertEquals(List.of(TraverseMode.WALK), stateValues(traversedState, State::getBackMode));
    +    assertEquals(1719, traversedState.getElapsedTimeSeconds());
    +  }
    +
    +  @Test
    +  void withMultipleStates() {
    +    // CAR_PICKUP creates parallel walking and driving states
    +    // This tests that of the two states (WALKING, CAR) the least weight (CAR) is selected
    +    var edge = StreetModelForTest
    +      .streetEdge(BERLIN_V, BRANDENBURG_GATE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.ALL)
    +      .buildAndConnect();
    +
    +    var edges = List.of(edge);
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.CAR_PICKUP)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(edge.getFromVertex()), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, edges).get();
    +
    +    assertEquals(List.of(TraverseMode.CAR), stateValues(traversedState, State::getBackMode));
    +    assertEquals(205, traversedState.getElapsedTimeSeconds());
    +  }
    +
    +  @Test
    +  void withDominatedStates() {
    +    // CAR_PICKUP creates parallel walking and driving states
    +    // This tests that the most optimal (walking and driving the last stretch) is found after
    +    // discarding the initial driving state for edge1
    +    var edge1 = StreetModelForTest
    +      .streetEdge(FERNSEHTURM_V, BERLIN_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.ALL)
    +      .buildAndConnect();
    +    var edge2 = StreetModelForTest
    +      .streetEdge(BERLIN_V, BRANDENBURG_GATE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.PEDESTRIAN)
    +      .buildAndConnect();
    +    var edge3 = StreetModelForTest
    +      .streetEdge(BRANDENBURG_GATE_V, ADMIRALBRUCKE_V)
    +      .toBuilder()
    +      .withPermission(StreetTraversalPermission.ALL)
    +      .buildAndConnect();
    +
    +    var edges = List.of(edge1, edge2, edge3);
    +    var request = StreetSearchRequest
    +      .of()
    +      .withStartTime(Instant.EPOCH)
    +      .withMode(StreetMode.CAR_PICKUP)
    +      .build();
    +    var initialStates = State.getInitialStates(Set.of(edge1.getFromVertex()), request);
    +    var traversedState = EdgeTraverser.traverseEdges(initialStates, edges).get();
    +
    +    assertEquals(
    +      List.of(88.103, 2286.029, 3444.28),
    +      stateValues(
    +        traversedState,
    +        state -> state.getBackEdge() != null ? state.getBackEdge().getDistanceMeters() : null
    +      )
    +    );
    +    assertEquals(
    +      List.of(TraverseMode.WALK, TraverseMode.WALK, TraverseMode.CAR),
    +      stateValues(traversedState, State::getBackMode)
    +    );
    +    assertEquals(2169, traversedState.getElapsedTimeSeconds());
    +  }
    +
    +  private  List stateValues(State state, Function extractor) {
    +    var values = new ArrayList();
    +    while (state != null) {
    +      var value = extractor.apply(state);
    +      if (value != null) {
    +        values.add(value);
    +      }
    +      state = state.getBackState();
    +    }
    +    return values.reversed();
    +  }
    +}
    diff --git a/application/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java b/application/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java
    index 9605d950ae0..7ea56f66145 100644
    --- a/application/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java
    +++ b/application/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java
    @@ -202,7 +202,7 @@ public TestStateBuilder elevator() {
     
         currentState =
           EdgeTraverser
    -        .traverseEdges(currentState, List.of(link, boardEdge, hopEdge, alightEdge))
    +        .traverseEdges(new State[] { currentState }, List.of(link, boardEdge, hopEdge, alightEdge))
             .orElseThrow();
         return this;
       }
    diff --git a/application/src/test/java/org/opentripplanner/transit/model/_data/TimetableRepositoryForTest.java b/application/src/test/java/org/opentripplanner/transit/model/_data/TimetableRepositoryForTest.java
    index 7d7652b52e6..f8dbf97efb9 100644
    --- a/application/src/test/java/org/opentripplanner/transit/model/_data/TimetableRepositoryForTest.java
    +++ b/application/src/test/java/org/opentripplanner/transit/model/_data/TimetableRepositoryForTest.java
    @@ -40,6 +40,7 @@
     import org.opentripplanner.transit.model.timetable.TripBuilder;
     import org.opentripplanner.transit.service.SiteRepository;
     import org.opentripplanner.transit.service.SiteRepositoryBuilder;
    +import org.opentripplanner.utils.time.TimeUtils;
     
     /**
      * Test utility class to help construct valid transit model objects.
    @@ -227,7 +228,8 @@ public Place place(String name, double lat, double lon) {
        * 

    * The first stop has stop sequence 10, the following one has 20 and so on. */ - public List stopTimesEvery5Minutes(int count, Trip trip, int startTime) { + public List stopTimesEvery5Minutes(int count, Trip trip, String time) { + var startTime = TimeUtils.time(time); return IntStream .range(0, count) .mapToObj(seq -> stopTime(trip, (seq + 1) * 10, startTime + (seq * 60 * 5))) diff --git a/application/src/test/java/org/opentripplanner/transit/model/network/TripPatternTest.java b/application/src/test/java/org/opentripplanner/transit/model/network/TripPatternTest.java index bb0ec765331..32563545694 100644 --- a/application/src/test/java/org/opentripplanner/transit/model/network/TripPatternTest.java +++ b/application/src/test/java/org/opentripplanner/transit/model/network/TripPatternTest.java @@ -24,7 +24,9 @@ class TripPatternTest { private static final Route ROUTE = TimetableRepositoryForTest.route("routeId").build(); public static final RegularStop STOP_A = TEST_MODEL.stop("A").build(); + public static final RegularStop STOP_X = TEST_MODEL.stop("X").build(); public static final RegularStop STOP_B = TEST_MODEL.stop("B").build(); + public static final RegularStop STOP_Y = TEST_MODEL.stop("Y").build(); public static final RegularStop STOP_C = TEST_MODEL.stop("C").build(); private static final StopPattern STOP_PATTERN = TimetableRepositoryForTest.stopPattern( STOP_A, @@ -33,8 +35,8 @@ class TripPatternTest { ); private static final List HOP_GEOMETRIES = List.of( - makeLineString(STOP_A.getCoordinate(), STOP_B.getCoordinate()), - makeLineString(STOP_B.getCoordinate(), STOP_C.getCoordinate()) + makeLineString(STOP_A.getCoordinate(), STOP_X.getCoordinate(), STOP_B.getCoordinate()), + makeLineString(STOP_B.getCoordinate(), STOP_Y.getCoordinate(), STOP_C.getCoordinate()) ); private static final TripPattern subject = TripPattern @@ -70,6 +72,27 @@ void copy() { assertEquals(HOP_GEOMETRIES.get(1), copy.getHopGeometry(1)); } + @Test + void hopGeometryForReplacementPattern() { + var pattern = TripPattern + .of(id("replacement")) + .withName("replacement") + .withRoute(ROUTE) + .withStopPattern(TimetableRepositoryForTest.stopPattern(STOP_A, STOP_B, STOP_X, STOP_Y)) + .withOriginalTripPattern(subject) + .build(); + + assertEquals(HOP_GEOMETRIES.get(0), pattern.getHopGeometry(0)); + assertEquals( + makeLineString(STOP_B.getCoordinate(), STOP_X.getCoordinate()), + pattern.getHopGeometry(1) + ); + assertEquals( + makeLineString(STOP_X.getCoordinate(), STOP_Y.getCoordinate()), + pattern.getHopGeometry(2) + ); + } + @Test void sameAs() { assertTrue(subject.sameAs(subject.copy().build())); diff --git a/application/src/test/java/org/opentripplanner/transit/model/site/RegularStopTest.java b/application/src/test/java/org/opentripplanner/transit/model/site/RegularStopTest.java index c2c50b24c9e..cfcab8a0b13 100644 --- a/application/src/test/java/org/opentripplanner/transit/model/site/RegularStopTest.java +++ b/application/src/test/java/org/opentripplanner/transit/model/site/RegularStopTest.java @@ -78,7 +78,7 @@ void copy() { assertEquals(WHEELCHAIR_ACCESSIBILITY, copy.getWheelchairAccessibility()); // TODO inconsistent naming assertEquals(NETEX_SUBMODE, copy.getNetexVehicleSubmode()); - assertEquals(VEHICLE_TYPE, copy.getGtfsVehicleType()); + assertEquals(VEHICLE_TYPE, copy.getVehicleType()); assertEquals(TIME_ZONE, copy.getTimeZone()); assertEquals(PLATFORM_CODE, copy.getPlatformCode()); } diff --git a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 1c0d2a03a0f..ac0530d1ef9 100644 --- a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.transit.service; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.opentripplanner.transit.model.basic.TransitMode.BUS; import static org.opentripplanner.transit.model.basic.TransitMode.FERRY; import static org.opentripplanner.transit.model.basic.TransitMode.RAIL; @@ -16,6 +17,7 @@ import org.opentripplanner.model.TimetableSnapshot; 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.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; @@ -121,4 +123,9 @@ void getPatternForStopsWithRealTime() { Collection patternsForStop = service.getPatternsForStop(STOP_B, true); assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN, REAL_TIME_PATTERN), patternsForStop); } + + @Test + void containsTrip() { + assertFalse(service.containsTrip(new FeedScopedId("x", "x"))); + } } diff --git a/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java b/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java index f0e87b046c5..e5e305d8081 100644 --- a/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java +++ b/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java @@ -144,7 +144,7 @@ public TestStatus status() { public static void main(String[] args) { try { - OtpStartupInfo.logInfo(); + OtpStartupInfo.logInfo("Run Speed Test"); // Given the following setup SpeedTestCmdLineOpts opts = new SpeedTestCmdLineOpts(args); var config = SpeedTestConfig.config(opts.rootDir()); diff --git a/application/src/test/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcherTest.java b/application/src/test/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcherTest.java index 4da03a6b517..fb1e196f7ae 100644 --- a/application/src/test/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcherTest.java +++ b/application/src/test/java/org/opentripplanner/updater/GtfsRealtimeFuzzyTripMatcherTest.java @@ -2,50 +2,134 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; import com.google.transit.realtime.GtfsRealtime.TripDescriptor; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.opentripplanner.GtfsTest; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.model.calendar.CalendarServiceData; +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.network.Route; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.SiteRepository; +import org.opentripplanner.transit.service.SiteRepositoryBuilder; +import org.opentripplanner.transit.service.TimetableRepository; -public class GtfsRealtimeFuzzyTripMatcherTest extends GtfsTest { +public class GtfsRealtimeFuzzyTripMatcherTest { - @Test - public void testMatch() { - String feedId = timetableRepository.getFeedIds().iterator().next(); + private static final String ROUTE_ID = "r1"; + private static final String FEED_ID = TimetableRepositoryForTest.FEED_ID; + private static final LocalDate SERVICE_DATE = LocalDate.of(2024, 11, 13); + private static final String GTFS_SERVICE_DATE = SERVICE_DATE.toString().replaceAll("-", ""); + private static final int SERVICE_CODE = 555; + private static final SiteRepositoryBuilder siteRepositoryBuilder = SiteRepository.of(); + private static final TimetableRepositoryForTest TEST_MODEL = new TimetableRepositoryForTest( + siteRepositoryBuilder + ); + private static final RegularStop STOP_1 = TEST_MODEL.stop("s1").build(); + private static final RegularStop STOP_2 = TEST_MODEL.stop("s2").build(); + private static final TimetableRepository TIMETABLE_REPOSITORY = new TimetableRepository( + siteRepositoryBuilder.build(), + new Deduplicator() + ); + private static final Route ROUTE = TimetableRepositoryForTest.route(id(ROUTE_ID)).build(); + private static final String TRIP_ID = "t1"; + private static final Trip TRIP = TimetableRepositoryForTest.trip(TRIP_ID).build(); + + private static final FeedScopedId SERVICE_ID = TimetableRepositoryForTest.id("sid1"); + private static final String START_TIME = "07:30:00"; + private static final RealTimeTripTimes TRIP_TIMES = TripTimesFactory.tripTimes( + TRIP, + TEST_MODEL.stopTimesEvery5Minutes(5, TRIP, START_TIME), + new Deduplicator() + ); + private static final TripPattern TRIP_PATTERN = TimetableRepositoryForTest + .tripPattern("tp1", ROUTE) + .withStopPattern(TimetableRepositoryForTest.stopPattern(STOP_1, STOP_2)) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(TRIP_TIMES)) + .build(); - GtfsRealtimeFuzzyTripMatcher matcher = new GtfsRealtimeFuzzyTripMatcher( - new DefaultTransitService(timetableRepository) + @BeforeAll + static void setup() { + TRIP_TIMES.setServiceCode(SERVICE_CODE); + CalendarServiceData calendarServiceData = new CalendarServiceData(); + calendarServiceData.putServiceDatesForServiceId(SERVICE_ID, List.of(SERVICE_DATE)); + TIMETABLE_REPOSITORY.addTripPattern(TRIP_PATTERN.getId(), TRIP_PATTERN); + TIMETABLE_REPOSITORY.getServiceCodes().put(SERVICE_ID, SERVICE_CODE); + TIMETABLE_REPOSITORY.updateCalendarServiceData( + true, + calendarServiceData, + DataImportIssueStore.NOOP ); - TripDescriptor trip1 = TripDescriptor + TIMETABLE_REPOSITORY.index(); + } + + @Test + void noTripId() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().build(); + assertEquals(TRIP_ID, matcher.match(FEED_ID, trip).getTripId()); + } + + @Test + void tripIdSetButNotInSchedule() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().setTripId("does-not-exist-in-schedule").build(); + assertEquals(TRIP_ID, matcher.match(FEED_ID, trip).getTripId()); + } + + @Test + void tripIdExistsInSchedule() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().setTripId(TRIP_ID).build(); + assertEquals(TRIP_ID, matcher.match(FEED_ID, trip).getTripId()); + } + + @Test + void incorrectRoute() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().setRouteId("does-not-exists").build(); + assertFalse(matcher.match(FEED_ID, trip).hasTripId()); + } + + @Test + void incorrectDateFormat() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().setStartDate("ZZZ").build(); + assertFalse(matcher.match(FEED_ID, trip).hasTripId()); + } + + @Test + void incorrectDirection() { + var matcher = matcher(); + TripDescriptor trip = matchingTripUpdate().setDirectionId(1).build(); + assertFalse(matcher.match(FEED_ID, trip).hasTripId()); + } + + @Test + void noMatch() { + // Test matching with "real time", when schedule uses time greater than 24:00 + var trip = TripDescriptor .newBuilder() - .setRouteId("1") + .setRouteId("4") .setDirectionId(0) - .setStartTime("06:47:00") + .setStartTime("12:00:00") .setStartDate("20090915") .build(); - assertEquals("10W1020", matcher.match(feedId, trip1).getTripId()); - trip1 = - TripDescriptor - .newBuilder() - .setRouteId("4") - .setDirectionId(0) - .setStartTime("00:02:00") - .setStartDate("20090915") - .build(); - assertEquals("40W1890", matcher.match(feedId, trip1).getTripId()); - // Test matching with "real time", when schedule uses time grater than 24:00 - trip1 = - TripDescriptor - .newBuilder() - .setRouteId("4") - .setDirectionId(0) - .setStartTime("12:00:00") - .setStartDate("20090915") - .build(); // No departure at this time - assertFalse(trip1.hasTripId()); - trip1 = + assertFalse(trip.hasTripId()); + trip = TripDescriptor .newBuilder() .setRouteId("1") @@ -53,11 +137,47 @@ public void testMatch() { .setStartDate("20090915") .build(); // Missing direction id - assertFalse(trip1.hasTripId()); + assertFalse(trip.hasTripId()); + } + + @Nested + class IncompleteData { + + @Test + void noRouteId() { + var td = matchingTripUpdate().clearRouteId().build(); + assertFalse(matcher().match(FEED_ID, td).hasTripId()); + } + + @Test + void noDirectionId() { + var td = matchingTripUpdate().clearDirectionId().build(); + assertFalse(matcher().match(FEED_ID, td).hasTripId()); + } + + @Test + void noStartDate() { + var td = matchingTripUpdate().clearStartDate().build(); + assertFalse(matcher().match(FEED_ID, td).hasTripId()); + } + + @Test + void noStartTime() { + var td = matchingTripUpdate().clearStartTime().build(); + assertFalse(matcher().match(FEED_ID, td).hasTripId()); + } } - @Override - public String getFeedName() { - return "portland/portland.gtfs.zip"; + private static GtfsRealtimeFuzzyTripMatcher matcher() { + return new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(TIMETABLE_REPOSITORY)); + } + + private static TripDescriptor.Builder matchingTripUpdate() { + return TripDescriptor + .newBuilder() + .setRouteId(ROUTE_ID) + .setDirectionId(2) + .setStartTime(START_TIME) + .setStartDate(GTFS_SERVICE_DATE); } } diff --git a/application/src/test/java/org/opentripplanner/updater/siri/AddedTripBuilderTest.java b/application/src/test/java/org/opentripplanner/updater/siri/AddedTripBuilderTest.java index 0c01e847148..abed2144982 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/AddedTripBuilderTest.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/AddedTripBuilderTest.java @@ -135,7 +135,8 @@ void testAddedTrip() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -247,7 +248,8 @@ void testAddedTripOnAddedRoute() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -275,7 +277,8 @@ void testAddedTripOnAddedRoute() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -317,7 +320,8 @@ void testAddedTripOnExistingRoute() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -351,7 +355,8 @@ void testAddedTripWithoutReplacedRoute() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -395,7 +400,8 @@ void testAddedTripFailOnMissingServiceId() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -450,7 +456,8 @@ void testAddedTripFailOnNonIncreasingDwellTime() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -489,7 +496,8 @@ void testAddedTripFailOnTooFewCalls() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); @@ -536,7 +544,8 @@ void testAddedTripFailOnUnknownStop() { false, SHORT_NAME, HEADSIGN, - List.of() + List.of(), + "DATASOURCE" ) .build(); diff --git a/application/src/test/java/org/opentripplanner/updater/siri/ModifiedTripBuilderTest.java b/application/src/test/java/org/opentripplanner/updater/siri/ModifiedTripBuilderTest.java index ad3ed749fb9..6c1797a24a2 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/ModifiedTripBuilderTest.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/ModifiedTripBuilderTest.java @@ -174,7 +174,8 @@ void testUpdateNoCalls() { List.of(), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -203,7 +204,8 @@ void testUpdateCancellation() { List.of(), true, null, - false + false, + "DATASOURCE" ) .build(); @@ -247,7 +249,8 @@ void testUpdateSameStops() { ), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -297,7 +300,8 @@ void testUpdateValidationFailure() { ), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -345,7 +349,8 @@ void testUpdateSameStopsDepartEarly() { ), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -395,7 +400,8 @@ void testUpdateUpdatedStop() { ), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -451,7 +457,8 @@ void testUpdateCascading() { ), false, null, - false + false, + "DATASOURCE" ) .build(); @@ -496,7 +503,8 @@ void testUpdateCascading() { ), false, null, - false + false, + "DATASOURCE" ) .build(); diff --git a/application/src/test/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandlerTest.java b/application/src/test/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandlerTest.java index 94c4af8d22e..3cd8e50c000 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandlerTest.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/SiriAlertsUpdateHandlerTest.java @@ -198,8 +198,8 @@ public void testSiriSxUpdateForStop() { final List alertUrlList = transitAlert.siriUrls(); AlertUrl alertUrl = alertUrlList.get(0); - assertEquals(infoLinkUri, alertUrl.uri); - assertEquals(infoLinkLabel, alertUrl.label); + assertEquals(infoLinkUri, alertUrl.uri()); + assertEquals(infoLinkLabel, alertUrl.label()); } @Test diff --git a/application/src/test/java/org/opentripplanner/updater/siri/SiriEtBuilder.java b/application/src/test/java/org/opentripplanner/updater/siri/SiriEtBuilder.java index b9517e987c4..737fb04917e 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/SiriEtBuilder.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/SiriEtBuilder.java @@ -39,6 +39,7 @@ public SiriEtBuilder(DateTimeHelper dateTimeHelper) { // Set default values evj.setMonitored(true); + evj.setDataSource("DATASOURCE"); } public List buildEstimatedTimetableDeliveries() { @@ -237,11 +238,15 @@ public EstimatedCallsBuilder(DateTimeHelper dateTimeHelper, int orderOffset) { } public EstimatedCallsBuilder call(StopLocation stop) { + return call(stop.getId().getId()); + } + + public EstimatedCallsBuilder call(String stopPointRef) { var call = new EstimatedCall(); call.setOrder(BigInteger.valueOf(orderOffset + calls.size())); var ref = new StopPointRef(); - ref.setValue(stop.getId().getId()); + ref.setValue(stopPointRef); call.setStopPointRef(ref); calls.add(call); diff --git a/application/src/test/java/org/opentripplanner/updater/siri/moduletests/rejection/InvalidStopPointRefTest.java b/application/src/test/java/org/opentripplanner/updater/siri/moduletests/rejection/InvalidStopPointRefTest.java new file mode 100644 index 00000000000..820f44e3a86 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/updater/siri/moduletests/rejection/InvalidStopPointRefTest.java @@ -0,0 +1,48 @@ +package org.opentripplanner.updater.siri.moduletests.rejection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.updater.siri.SiriEtBuilder; +import org.opentripplanner.updater.spi.UpdateError; +import org.opentripplanner.updater.trip.RealtimeTestConstants; +import org.opentripplanner.updater.trip.RealtimeTestEnvironment; + +public class InvalidStopPointRefTest implements RealtimeTestConstants { + + private static Stream cases() { + return Stream + .of("", " ", " ", "\n", "null", "\t", null) + .flatMap(id -> Stream.of(Arguments.of(id, true), Arguments.of(id, false))); + } + + @ParameterizedTest(name = "invalid id of ''{0}'', extraJourney={1}") + @MethodSource("cases") + void rejectEmptyStopPointRef(String invalidRef, boolean extraJourney) { + var env = RealtimeTestEnvironment.siri().build(); + + // journey contains empty stop point ref elements + // happens in the South Tyrolian feed: https://github.com/noi-techpark/odh-mentor-otp/issues/213 + var invalidJourney = new SiriEtBuilder(env.getDateTimeHelper()) + .withEstimatedVehicleJourneyCode("invalid-journey") + .withOperatorRef("unknown-operator") + .withLineRef("unknown-line") + .withIsExtraJourney(extraJourney) + .withEstimatedCalls(builder -> + builder + .call(invalidRef) + .departAimedExpected("10:58", "10:48") + .call(invalidRef) + .arriveAimedExpected("10:08", "10:58") + ) + .buildEstimatedTimetableDeliveries(); + + var result = env.applyEstimatedTimetable(invalidJourney); + assertEquals(0, result.successful()); + assertFailure(UpdateError.UpdateErrorType.EMPTY_STOP_POINT_REF, result); + } +} diff --git a/application/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java b/application/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java index 5fa7c5e8df0..9cdeba6ec3d 100644 --- a/application/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java +++ b/application/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java @@ -1,7 +1,6 @@ package org.opentripplanner.updater.vehicle_position; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.model.plan.PlanTestConstants.T11_00; import static org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig.VehiclePositionFeature.OCCUPANCY; import static org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig.VehiclePositionFeature.POSITION; import static org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig.VehiclePositionFeature.STOP_POSITION; @@ -92,7 +91,7 @@ public void tripNotFoundInPattern() { var trip1 = TimetableRepositoryForTest.trip(tripId).build(); var trip2 = TimetableRepositoryForTest.trip(secondTripId).build(); - var stopTimes = testModel.stopTimesEvery5Minutes(3, trip1, T11_00); + var stopTimes = testModel.stopTimesEvery5Minutes(3, trip1, "11:00"); var pattern = tripPattern(trip1, stopTimes); // Map positions to trips in feed diff --git a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java index 2ca93a10f28..f48faef3180 100644 --- a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java +++ b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java @@ -20,6 +20,7 @@ import org.opentripplanner.updater.GraphWriterRunnable; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDatasource; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; import org.opentripplanner.updater.vehicle_rental.datasources.params.VehicleRentalDataSourceParameters; class VehicleRentalUpdaterTest { @@ -103,5 +104,10 @@ public VehicleRentalSourceType sourceType() { public HttpHeaders httpHeaders() { return HttpHeaders.empty(); } + + @Override + public boolean allowRentalType(RentalPickupType rentalPickupType) { + return true; + } } } diff --git a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java index 7b062bcc17c..b238b22f305 100644 --- a/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java +++ b/application/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; +import org.opentripplanner.updater.vehicle_rental.datasources.params.RentalPickupType; /** * This tests the mapping between data coming from a {@link GbfsFeedLoader} to OTP station models. @@ -32,7 +33,8 @@ void makeStationFromV22() { HttpHeaders.empty(), null, false, - false + false, + RentalPickupType.ALL ), new OtpHttpClientFactory() ); @@ -123,7 +125,8 @@ void geofencing() { HttpHeaders.empty(), null, true, - false + false, + RentalPickupType.ALL ), new OtpHttpClientFactory() ); @@ -165,7 +168,8 @@ void makeStationFromV10() { HttpHeaders.empty(), network, false, - true + true, + RentalPickupType.ALL ), new OtpHttpClientFactory() ); diff --git a/application/src/test/resources/gtfs/simple/routes.txt b/application/src/test/resources/gtfs/simple/routes.txt index ba2bb397941..2957341851e 100755 --- a/application/src/test/resources/gtfs/simple/routes.txt +++ b/application/src/test/resources/gtfs/simple/routes.txt @@ -1,13 +1,13 @@ -route_id,route_short_name,route_long_name,route_type,route_bikes_allowed -1,1,1,3,2 -2,2,2,3,1 +route_id,route_short_name,route_long_name,route_type,bikes_allowed +1,1,1,3,1 +2,2,2,3,2 3,3,3,3, 4,4,4,7, 5,5,5,2, 6,6,6,2, 7,7,7,2, 8,8,8,3, -9,9,9,2,2 +9,9,9,2,1 10,10,10,2, 11,11,11,2, 12,12,12,2, diff --git a/application/src/test/resources/gtfs/simple/trips.txt b/application/src/test/resources/gtfs/simple/trips.txt index d18b02d6896..ca826df2c5c 100755 --- a/application/src/test/resources/gtfs/simple/trips.txt +++ b/application/src/test/resources/gtfs/simple/trips.txt @@ -1,9 +1,9 @@ -route_id,service_id,trip_id,shape_id,block_id,wheelchair_accessible,trip_bikes_allowed,direction_id,trip_headsign +route_id,service_id,trip_id,shape_id,block_id,wheelchair_accessible,bikes_allowed,direction_id,trip_headsign 1,alldays,1.1,,,1,,,foo 1,alldays,1.2,,,1,,,foo 1,alldays,1.3,,,1,,,foo -2,alldays,2.1,,,0,2,,foo -2,alldays,2.2,,,0,2,,foo +2,alldays,2.1,,,0,1,,foo +2,alldays,2.2,,,0,1,,foo 3,alldays,3.1,,,1,,,foo 3,alldays,3.2,,,1,,,foo 4,weekdays,4.1,4,,,,,foo @@ -15,7 +15,7 @@ route_id,service_id,trip_id,shape_id,block_id,wheelchair_accessible,trip_bikes_a 6,alldays,6.2,,block.2,,,,foo 7,alldays,7.2,,block.2,,,,foo 8,alldays,8.1,,block.2,,,,foo -9,alldays,9.1,,,,1,,foo +9,alldays,9.1,,,,2,,foo 10,alldays,10.1,,,,,,foo 10,alldays,10.2,,,,,,foo 10,alldays,10.3,,,,,,foo diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json index eb7b3d24154..1683a37dbe9 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/alerts.json @@ -1,63 +1,41 @@ { - "data" : { - "alerts" : [ + "data": { + "alerts": [ { - "id" : "QWxlcnQ6RjpuZWl0aGVyLWhlYWRlci1ub3ItZGVzY3JpcHRpb24", - "alertHeaderText" : "", - "alertDescriptionText" : "", - "alertUrl" : null, - "alertDescriptionTextTranslations" : [ ], - "alertHeaderTextTranslations" : [ ] + "id": "QWxlcnQ6RjpuZWl0aGVyLWhlYWRlci1ub3ItZGVzY3JpcHRpb24", + "headerDefault": "", + "headerDe": "", + "descriptionDefault": "", + "descriptionDe": "", + "urlDefault": null, + "urlDe": null }, { - "id" : "QWxlcnQ6Rjpuby1oZWFkZXI", - "alertHeaderText" : "Second string", - "alertDescriptionText" : "Second string", - "alertUrl" : null, - "alertDescriptionTextTranslations" : [ - { - "language" : null, - "text" : "Second string" - }, - { - "language" : "de", - "text" : "Zweite Zeichenabfolge" - }, - { - "language" : "fi", - "text" : "Etkö ole varma, mitä tämä tarkoittaa" - } - ], - "alertHeaderTextTranslations" : [ ] + "id": "QWxlcnQ6Rjpuby1oZWFkZXI", + "headerDefault": "Second string", + "headerDe": "Zweite Zeichenabfolge", + "descriptionDefault": "Second string", + "descriptionDe": "Zweite Zeichenabfolge", + "urlDefault": null, + "urlDe": null }, { - "id" : "QWxlcnQ6Rjphbi1hbGVydA", - "alertHeaderText" : "A header", - "alertDescriptionText" : "A description", - "alertUrl" : "https://example.com", - "alertDescriptionTextTranslations" : [ ], - "alertHeaderTextTranslations" : [ ] + "id": "QWxlcnQ6Rjphbi1hbGVydA", + "headerDefault": "A header", + "headerDe": "A header", + "descriptionDefault": "A description", + "descriptionDe": "A description", + "urlDefault": "https://example.com", + "urlDe": "https://example.com" }, { - "id" : "QWxlcnQ6Rjpuby1kZXNjcmlwdGlvbg", - "alertHeaderText" : "First string", - "alertDescriptionText" : "First string", - "alertUrl" : null, - "alertDescriptionTextTranslations" : [ ], - "alertHeaderTextTranslations" : [ - { - "text" : "First string", - "language" : null - }, - { - "text" : "Erste Zeichenabfolge", - "language" : "de" - }, - { - "text" : "Minulla ei ole aavistustakaan kuinka puhua suomea", - "language" : "fi" - } - ] + "id": "QWxlcnQ6Rjpuby1kZXNjcmlwdGlvbg", + "headerDefault": "First string", + "headerDe": "Erste Zeichenabfolge", + "descriptionDefault": "First string", + "descriptionDe": "Erste Zeichenabfolge", + "urlDefault": null, + "urlDe": null } ] } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/node-alert.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/node-alert.json new file mode 100644 index 00000000000..8d4c96fb4a9 --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/node-alert.json @@ -0,0 +1,10 @@ +{ + "data": { + "node": { + "id": "QWxlcnQ6Rjpuby1oZWFkZXI", + "alertHeaderText": "Second string", + "alertDescriptionText": "Second string", + "alertUrl": null + } + } +} \ No newline at end of file diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json new file mode 100644 index 00000000000..01422dd6580 --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rentals-bybbox.json @@ -0,0 +1,83 @@ +{ + "data": { + "vehicleRentalsByBbox": [ + { + "__typename": "VehicleRentalStation", + "stationId": "Network-1:FooStation", + "name": "FooStation", + "vehiclesAvailable": 10, + "availableVehicles": { + "byType": [ + { + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "ELECTRIC" + }, + "count": 5 + }, + { + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "HUMAN" + }, + "count": 5 + } + ], + "total": 10 + }, + "spacesAvailable": 10, + "availableSpaces": { + "byType": [ + { + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "ELECTRIC" + }, + "count": 3 + }, + { + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "HUMAN" + }, + "count": 7 + } + ], + "total": 10 + }, + "allowDropoff": false, + "allowPickup": false, + "allowDropoffNow": false, + "allowPickupNow": false, + "lon": 18.99, + "lat": 47.51, + "capacity": null, + "allowOverloading": false, + "rentalUris": null, + "operative": false, + "rentalNetwork": { + "networkId": "Network-1", + "url": "https://foo.bar" + } + }, + { + "__typename": "RentalVehicle", + "vehicleId": "Network-1:free-floating-bicycle", + "name": "free-floating-bicycle", + "allowPickupNow": true, + "lon": 19.01, + "lat": 47.52, + "rentalUris": null, + "operative": true, + "vehicleType": { + "formFactor": "BICYCLE", + "propulsionType": "HUMAN" + }, + "rentalNetwork": { + "networkId": "Network-1", + "url": "https://foo.bar" + } + } + ] + } +} \ No newline at end of file diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql index 33ff47dd33b..3a5d438e8ef 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/alerts.graphql @@ -1,18 +1,11 @@ { alerts { id - alertHeaderText - alertDescriptionText - alertUrl - # these translations are a bit questionable, the above fields are already translated into the - # language selected in the request - alertDescriptionTextTranslations { - language - text - } - alertHeaderTextTranslations { - text - language - } + headerDefault: alertHeaderText + headerDe: alertHeaderText(language: "de") + descriptionDefault: alertDescriptionText + descriptionDe: alertDescriptionText(language: "de") + urlDefault: alertUrl + urlDe: alertUrl(language: "de") } } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/node-alert.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/node-alert.graphql new file mode 100644 index 00000000000..533595e0d5f --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/node-alert.graphql @@ -0,0 +1,10 @@ +{ + node(id: "QWxlcnQ6Rjpuby1oZWFkZXI") { + id + ... on Alert { + alertHeaderText + alertDescriptionText + alertUrl + } + } +} diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql index 6a51cc10d2c..5de2d2fdfef 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql @@ -10,6 +10,16 @@ filters: [{ select: [{ tags: ["e"] }] }] } transportModes: [{ mode: CAR, qualifier: HAIL }] + via: [ + { passThrough: { label: "via1", stopLocationIds: ["F:BUS"] } } + { + visit: { + label: "via2" + stopLocationIds: ["F:RAIL"] + minimumWaitTime: "1h" + } + } + ] ) { itineraries { start diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql index e0266281aa4..1bbf9135321 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql @@ -11,6 +11,16 @@ location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } label: "Work" } + via: [ + { passThrough: { label: "via1", stopLocationIds: ["F:BUS"] } } + { + visit: { + label: "via2" + stopLocationIds: ["F:RAIL"] + minimumWaitTime: "1h" + } + } + ] modes: { directOnly: false transitOnly: false diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql new file mode 100644 index 00000000000..26209f427f9 --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rentals-bybbox.graphql @@ -0,0 +1,75 @@ +{ + vehicleRentalsByBbox( + maximumLatitude: 48.00 + maximumLongitude: 19.10 + minimumLatitude: 47.50 + minimumLongitude: 18.80 + ) { + __typename + ... on RentalVehicle { + vehicleId + name + allowPickupNow + lon + lat + rentalUris { + android + ios + web + } + operative + vehicleType { + formFactor + propulsionType + } + rentalNetwork { + networkId + url + } + } + ... on VehicleRentalStation { + stationId + name + vehiclesAvailable + availableVehicles { + byType { + vehicleType { + formFactor + propulsionType + } + count + } + total + } + spacesAvailable + availableSpaces { + byType { + vehicleType { + formFactor + propulsionType + } + count + } + total + } + allowDropoff + allowPickup + allowDropoffNow + allowPickupNow + lon + lat + capacity + allowOverloading + rentalUris { + android + ios + web + } + operative + rentalNetwork { + networkId + url + } + } + } +} diff --git a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 8a0e457396e..2cf936a1e63 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -28,14 +28,14 @@ } }, { - "id" : "NONE", + "id" : "wheelchair-accessible", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#000", + "line-color" : "#136b04", "line-width" : [ "interpolate", [ @@ -58,33 +58,33 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ "==", - "permission", - "NONE" + "wheelchairAccessible", + true ], "layout" : { "line-cap" : "round", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Wheelchair accessibility" } }, { - "id" : "PEDESTRIAN", + "id" : "wheelchair-inaccessible", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", "minzoom" : 6, "maxzoom" : 23, "paint" : { - "line-color" : "#2ba812", + "line-color" : "#fc0f2a", "line-width" : [ "interpolate", [ @@ -107,33 +107,56 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ "==", - "permission", - "PEDESTRIAN" + "wheelchairAccessible", + false ], "layout" : { "line-cap" : "round", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Wheelchair accessibility" } }, { - "id" : "BICYCLE", + "id" : "no-thru-traffic PEDESTRIAN", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#10d3b6", + "line-color" : [ + "match", + [ + "get", + "noThruTraffic" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -156,33 +179,76 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "BICYCLE" + "any", + [ + "in", + "PEDESTRIAN", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "No-thru traffic" } }, { - "id" : "PEDESTRIAN_AND_BICYCLE", + "id" : "no-thru-traffic BICYCLE", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#10d3b6", + "line-color" : [ + "match", + [ + "get", + "noThruTraffic" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -205,33 +271,76 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "PEDESTRIAN_AND_BICYCLE" + "any", + [ + "in", + "BICYCLE", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "No-thru traffic" } }, { - "id" : "CAR", + "id" : "no-thru-traffic CAR", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#f92e13", + "line-color" : [ + "match", + [ + "get", + "noThruTraffic" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -254,33 +363,134 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "CAR" + "any", + [ + "in", + "CAR", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", + "visibility" : "none" + }, + "metadata" : { + "group" : "No-thru traffic" + } + }, + { + "id" : "no-thru-traffic-text", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "symbol", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ + "in", + "class", + "StreetEdge", + "AreaEdge", + "EscalatorEdge", + "PathwayEdge", + "ElevatorHopEdge", + "TemporaryPartialStreetEdge", + "TemporaryFreeEdge" + ], + "layout" : { + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, + "text-field" : "{noThruTraffic}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "text-overlap" : "never", + "text-offset" : [ + 0, + 1.0 + ], "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "No-thru traffic" } }, { - "id" : "PEDESTRIAN_AND_CAR", + "id" : "permission PEDESTRIAN", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#e25f8f", + "line-color" : [ + "match", + [ + "get", + "permission" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -303,33 +513,76 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "PEDESTRIAN_AND_CAR" + "any", + [ + "in", + "PEDESTRIAN", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Permissions" } }, { - "id" : "BICYCLE_AND_CAR", + "id" : "permission BICYCLE", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#e25f8f", + "line-color" : [ + "match", + [ + "get", + "permission" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -352,33 +605,76 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "BICYCLE_AND_CAR" + "any", + [ + "in", + "BICYCLE", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Permissions" } }, { - "id" : "ALL", + "id" : "permission CAR", "source" : "vectorSource", "source-layer" : "edges", "type" : "line", - "minzoom" : 6, + "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#adb2b0", + "line-color" : [ + "match", + [ + "get", + "permission" + ], + "NONE", + "#140d0e", + "PEDESTRIAN", + "#2ba812", + "BICYCLE", + "#10d3b6", + "PEDESTRIAN BICYCLE", + "#10d3b6", + "CAR", + "#f92e13", + "PEDESTRIAN CAR", + "#e25f8f", + "BICYCLE CAR", + "#e25f8f", + "ALL", + "#adb2b0", + "#140d0e" + ], "line-width" : [ "interpolate", [ @@ -401,22 +697,42 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ - "==", - "permission", - "ALL" + "any", + [ + "in", + "CAR", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], "layout" : { - "line-cap" : "round", + "line-cap" : "butt", "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Permissions" } }, { @@ -474,7 +790,7 @@ "visibility" : "none" }, "metadata" : { - "group" : "Traversal permissions" + "group" : "Permissions" } }, { @@ -495,9 +811,9 @@ "zoom" ], 13, - 0.2, + 0.1, 23, - 8.0 + 6.0 ], "line-offset" : [ "interpolate", @@ -508,9 +824,9 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ @@ -617,9 +933,9 @@ "zoom" ], 13, - 0.3, + 0.4, 23, - 6.0 + 7.0 ] }, "filter" : [ diff --git a/application/src/test/resources/speedtest/travelSearch-expected-results-bd.csv b/application/src/test/resources/speedtest/travelSearch-expected-results-bd.csv index 117eecc4a12..b38e60d5edb 100644 --- a/application/src/test/resources/speedtest/travelSearch-expected-results-bd.csv +++ b/application/src/test/resources/speedtest/travelSearch-expected-results-bd.csv @@ -1,3 +1,3 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s -2,0,43m25s,-1,0,12:00:00,12:43:25,,,,,Unknown transit 0tx 43m25s +1,0,42m16s,-1,0,12:00:00,12:42:16,,,,,Unknown transit 0tx 42m16s +2,0,42m16s,-1,0,12:00:00,12:42:16,,,,,Unknown transit 0tx 42m16s diff --git a/application/src/test/resources/speedtest/travelSearch-expected-results-btr.csv b/application/src/test/resources/speedtest/travelSearch-expected-results-btr.csv index 634b7b15b1e..db49235d649 100644 --- a/application/src/test/resources/speedtest/travelSearch-expected-results-btr.csv +++ b/application/src/test/resources/speedtest/travelSearch-expected-results-btr.csv @@ -1,5 +1,3 @@ tcId,nTransfers,duration,cost,walkDistance,startTime,endTime,agencies,modes,routes,stops,details -1,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s -1,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s -2,0,1h11m14s,-1,0,12:48:46,14:00:00,,,,,Unknown transit 0tx 1h11m14s -2,1,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 1tx 1h9m14s +1,0,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 0tx 1h9m14s +2,0,1h9m14s,-1,0,12:50:46,14:00:00,,,,,Unknown transit 0tx 1h9m14s diff --git a/client/package-lock.json b/client/package-lock.json index a438e9403fd..e1e00b4e99d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -23,14 +23,14 @@ "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.0", "@graphql-codegen/introspection": "4.0.3", - "@parcel/watcher": "2.4.1", + "@parcel/watcher": "2.5.0", "@testing-library/react": "16.0.1", "@types/react": "18.3.12", "@types/react-dom": "18.3.1", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.3", - "@vitest/coverage-v8": "2.1.4", + "@vitest/coverage-v8": "2.1.5", "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", @@ -41,8 +41,8 @@ "jsdom": "25.0.1", "prettier": "3.3.3", "typescript": "5.6.3", - "vite": "5.4.10", - "vitest": "2.1.4" + "vite": "5.4.11", + "vitest": "2.1.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2767,10 +2767,12 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", - "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, + "hasInstallScript": true, + "license": "MIT", "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -2785,28 +2787,30 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.4.1", - "@parcel/watcher-darwin-arm64": "2.4.1", - "@parcel/watcher-darwin-x64": "2.4.1", - "@parcel/watcher-freebsd-x64": "2.4.1", - "@parcel/watcher-linux-arm-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-musl": "2.4.1", - "@parcel/watcher-linux-x64-glibc": "2.4.1", - "@parcel/watcher-linux-x64-musl": "2.4.1", - "@parcel/watcher-win32-arm64": "2.4.1", - "@parcel/watcher-win32-ia32": "2.4.1", - "@parcel/watcher-win32-x64": "2.4.1" + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", - "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -2820,13 +2824,14 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2840,13 +2845,14 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", - "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2860,13 +2866,14 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", - "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -2880,13 +2887,35 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", - "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2900,13 +2929,14 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", - "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2920,13 +2950,14 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2940,13 +2971,14 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", - "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2960,13 +2992,14 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2980,13 +3013,14 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", - "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3000,13 +3034,14 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", - "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3020,13 +3055,14 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", - "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3816,9 +3852,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.4.tgz", - "integrity": "sha512-FPKQuJfR6VTfcNMcGpqInmtJuVXFSCd9HQltYncfR01AzXhLucMEtQ5SinPdZxsT5x/5BK7I5qFJ5/ApGCmyTQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.5.tgz", + "integrity": "sha512-/RoopB7XGW7UEkUndRXF87A9CwkoZAJW01pj8/3pgmDVsjMH2IKy6H1A38po9tmUlwhSyYs0az82rbKd9Yaynw==", "dev": true, "license": "MIT", "dependencies": { @@ -3831,7 +3867,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.12", "magicast": "^0.3.5", - "std-env": "^3.7.0", + "std-env": "^3.8.0", "test-exclude": "^7.0.1", "tinyrainbow": "^1.2.0" }, @@ -3839,8 +3875,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.4", - "vitest": "2.1.4" + "@vitest/browser": "2.1.5", + "vitest": "2.1.5" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3849,14 +3885,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", - "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz", + "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/spy": "2.1.5", + "@vitest/utils": "2.1.5", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -3865,13 +3901,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", - "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz", + "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", + "@vitest/spy": "2.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -3892,9 +3928,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz", + "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==", "dev": true, "license": "MIT", "dependencies": { @@ -3905,13 +3941,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", - "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz", + "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.4", + "@vitest/utils": "2.1.5", "pathe": "^1.1.2" }, "funding": { @@ -3919,13 +3955,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", - "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz", + "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.5", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -3934,9 +3970,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", - "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz", + "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==", "dev": true, "license": "MIT", "dependencies": { @@ -3947,13 +3983,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", + "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.5", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -5466,6 +5502,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -9872,10 +9915,11 @@ "dev": true }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" }, "node_modules/streamsearch": { "version": "1.1.0", @@ -10687,9 +10731,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10747,14 +10791,15 @@ } }, "node_modules/vite-node": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", - "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz", + "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -10769,31 +10814,31 @@ } }, "node_modules/vitest": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", - "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz", + "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.4", - "@vitest/mocker": "2.1.4", - "@vitest/pretty-format": "^2.1.4", - "@vitest/runner": "2.1.4", - "@vitest/snapshot": "2.1.4", - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/expect": "2.1.5", + "@vitest/mocker": "2.1.5", + "@vitest/pretty-format": "^2.1.5", + "@vitest/runner": "2.1.5", + "@vitest/snapshot": "2.1.5", + "@vitest/spy": "2.1.5", + "@vitest/utils": "2.1.5", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", - "std-env": "^3.7.0", + "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.4", + "vite-node": "2.1.5", "why-is-node-running": "^2.3.0" }, "bin": { @@ -10808,8 +10853,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.4", - "@vitest/ui": "2.1.4", + "@vitest/browser": "2.1.5", + "@vitest/ui": "2.1.5", "happy-dom": "*", "jsdom": "*" }, diff --git a/client/package.json b/client/package.json index 9753fae9777..d511ba477bc 100644 --- a/client/package.json +++ b/client/package.json @@ -32,14 +32,14 @@ "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.0", "@graphql-codegen/introspection": "4.0.3", - "@parcel/watcher": "2.4.1", + "@parcel/watcher": "2.5.0", "@testing-library/react": "16.0.1", "@types/react": "18.3.12", "@types/react-dom": "18.3.1", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.3", - "@vitest/coverage-v8": "2.1.4", + "@vitest/coverage-v8": "2.1.5", "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", @@ -50,7 +50,7 @@ "jsdom": "25.0.1", "prettier": "3.3.3", "typescript": "5.6.3", - "vite": "5.4.10", - "vitest": "2.1.4" + "vite": "5.4.11", + "vitest": "2.1.5" } } diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 51f0821b3e4..d23563331b5 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -7,6 +7,16 @@ import { ItineraryGraphiQLLineLink } from './ItineraryGraphiQLLineLink.tsx'; import { ItineraryGraphiQLQuayLink } from './ItineraryGraphiQLQuayLink.tsx'; import { ItineraryGraphiQLAuthorityLink } from './ItineraryGraphiQLAuthorityLink.tsx'; +/** + * Some GTFS trips don't have a short name (public code) so we use the long name in this case. + */ +function legName(leg: Leg): string { + if (leg.line?.publicCode) { + return leg.line.publicCode + ' ' + leg.toEstimatedCall?.destinationDisplay?.frontText; + } else { + return leg.line?.name || 'unknown'; + } +} export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean }) { return (

    @@ -20,10 +30,7 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean {leg.mode}{' '} {leg.line && ( <> - + , )}{' '} diff --git a/client/src/components/MapView/GeometryPropertyPopup.tsx b/client/src/components/MapView/GeometryPropertyPopup.tsx index 968dfdcde76..10245a8e0cf 100644 --- a/client/src/components/MapView/GeometryPropertyPopup.tsx +++ b/client/src/components/MapView/GeometryPropertyPopup.tsx @@ -23,7 +23,7 @@ export function GeometryPropertyPopup({ {Object.entries(properties).map(([key, value]) => ( {key} - {value} + {String(value)} ))} diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index d6be2d641d7..61dd48e4a34 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -4,6 +4,7 @@ import { IControl, Map, TypedStyleLayer } from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; + setInteractiveLayerIds: (interactiveLayerIds: string[]) => void; }; /** @@ -15,6 +16,12 @@ type LayerControlProps = { class LayerControl implements IControl { private readonly container: HTMLDivElement = document.createElement('div'); + private readonly setInteractiveLayerIds: (interactiveLayerIds: string[]) => void; + + constructor(setInteractiveLayerIds: (interactiveLayerIds: string[]) => void) { + this.setInteractiveLayerIds = setInteractiveLayerIds; + } + onAdd(map: Map) { this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; @@ -32,10 +39,8 @@ class LayerControl implements IControl { map .getLayersOrder() .map((l) => map.getLayer(l)) - .filter((s) => s?.type !== 'raster') - // the polylines of the routing result are put in map layers called jsx-1, jsx-2... - // we don't want them to show up in the debug layer selector - .filter((s) => !s?.id.startsWith('jsx')) + .filter((layer) => !!layer) + .filter((layer) => this.layerInteractive(layer)) .reverse() .forEach((layer) => { if (layer) { @@ -57,11 +62,24 @@ class LayerControl implements IControl { } } }); + // initialize clickable layers (initially stops) + this.updateInteractiveLayerIds(map); }); return this.container; } + private updateInteractiveLayerIds(map: Map) { + const visibleInteractiveLayerIds = map + .getLayersOrder() + .map((l) => map.getLayer(l)) + .filter((layer) => !!layer) + .filter((layer) => this.layerVisible(map, layer) && this.layerInteractive(layer)) + .map((layer) => layer.id); + + this.setInteractiveLayerIds(visibleInteractiveLayerIds); + } + private buildLayerDiv(layer: TypedStyleLayer, map: Map) { const layerDiv = document.createElement('div'); layerDiv.className = 'layer'; @@ -77,6 +95,7 @@ class LayerControl implements IControl { } else { map.setLayoutProperty(layer.id, 'visibility', 'none'); } + this.updateInteractiveLayerIds(map); }; input.checked = this.layerVisible(map, layer); input.className = 'layer'; @@ -118,13 +137,19 @@ class LayerControl implements IControl { return map.getLayoutProperty(layer.id, 'visibility') !== 'none'; } + private layerInteractive(layer: { id: string; type: string }) { + // the polylines of the routing result are put in map layers called jsx-1, jsx-2... + // we don't want them to show up in the debug layer selector + return layer?.type !== 'raster' && !layer?.id.startsWith('jsx'); + } + onRemove() { this.container.parentNode?.removeChild(this.container); } } export default function DebugLayerControl(props: LayerControlProps) { - useControl(() => new LayerControl(), { + useControl(() => new LayerControl(props.setInteractiveLayerIds), { position: props.position, }); diff --git a/client/src/components/MapView/LegLines.tsx b/client/src/components/MapView/LegLines.tsx index 8f6e8405997..2e34596f742 100644 --- a/client/src/components/MapView/LegLines.tsx +++ b/client/src/components/MapView/LegLines.tsx @@ -28,10 +28,21 @@ export function LegLines({ tripPattern }: { tripPattern?: TripPattern }) { 'line-cap': 'round', }} paint={{ - 'line-color': getColorForLeg(leg), + 'line-color': '#000', 'line-width': 5, }} /> + ), )} diff --git a/client/src/components/MapView/MapView.tsx b/client/src/components/MapView/MapView.tsx index 4a6080a1b45..8eb66f8c446 100644 --- a/client/src/components/MapView/MapView.tsx +++ b/client/src/components/MapView/MapView.tsx @@ -37,6 +37,7 @@ export function MapView({ const onMapDoubleClick = useMapDoubleClick({ tripQueryVariables, setTripQueryVariables }); const [showContextPopup, setShowContextPopup] = useState(null); const [showPropsPopup, setShowPropsPopup] = useState(null); + const [interactiveLayerIds, setInteractiveLayerIds] = useState([]); const [cursor, setCursor] = useState('auto'); const onMouseEnter = useCallback(() => setCursor('pointer'), []); const onMouseLeave = useCallback(() => setCursor('auto'), []); @@ -76,9 +77,7 @@ export function MapView({ onContextMenu={(e) => { setShowContextPopup(e.lngLat); }} - // it's unfortunate that you have to list these layers here. - // maybe there is a way around it: https://github.com/visgl/react-map-gl/discussions/2343 - interactiveLayerIds={['regular-stop', 'area-stop', 'group-stop', 'parking-vertex', 'vertex', 'edge', 'link']} + interactiveLayerIds={interactiveLayerIds} cursor={cursor} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} @@ -97,7 +96,7 @@ export function MapView({ setTripQueryVariables={setTripQueryVariables} loading={loading} /> - + {tripQueryResult?.trip.tripPatterns.length && ( )} diff --git a/client/src/components/MapView/NavigationMarkers.tsx b/client/src/components/MapView/NavigationMarkers.tsx index 6d043038ae9..2ae62cab8f2 100644 --- a/client/src/components/MapView/NavigationMarkers.tsx +++ b/client/src/components/MapView/NavigationMarkers.tsx @@ -2,6 +2,7 @@ import { TripQueryVariables } from '../../gql/graphql.ts'; import { Marker } from 'react-map-gl'; import markerFlagStart from '../../static/img/marker-flag-start-shadowed.png'; import markerFlagEnd from '../../static/img/marker-flag-end-shadowed.png'; +import { useCoordinateResolver } from './useCoordinateResolver.ts'; export function NavigationMarkers({ setCursor, @@ -14,13 +15,16 @@ export function NavigationMarkers({ setTripQueryVariables: (variables: TripQueryVariables) => void; loading: boolean; }) { + const fromCoordinates = useCoordinateResolver(tripQueryVariables.from); + const toCoordinates = useCoordinateResolver(tripQueryVariables.to); + return ( <> - {tripQueryVariables.from.coordinates && ( + {fromCoordinates && ( setCursor('grabbing')} onDragEnd={(e) => { setCursor('auto'); @@ -36,11 +40,11 @@ export function NavigationMarkers({ )} - {tripQueryVariables.to.coordinates && ( + {toCoordinates && ( setCursor('grabbing')} onDragEnd={(e) => { setCursor('auto'); diff --git a/client/src/components/MapView/useCoordinateResolver.ts b/client/src/components/MapView/useCoordinateResolver.ts new file mode 100644 index 00000000000..e44472df549 --- /dev/null +++ b/client/src/components/MapView/useCoordinateResolver.ts @@ -0,0 +1,30 @@ +import { Location } from '../../gql/graphql.ts'; +import { useQuayCoordinateQuery } from '../../hooks/useQuayCoordinateQuery.ts'; + +interface Coordinates { + latitude: number; + longitude: number; +} + +export function useCoordinateResolver(location: Location): Coordinates | undefined { + const quay = useQuayCoordinateQuery(location); + + if (quay) { + const { latitude, longitude } = quay; + + if (latitude && longitude) { + return { + latitude, + longitude, + }; + } + } + + if (location.coordinates) { + return { + ...location.coordinates, + }; + } + + return undefined; +} diff --git a/client/src/components/SearchBar/LocationInputField.tsx b/client/src/components/SearchBar/LocationInputField.tsx index bfa707776f1..70584e0e6d8 100644 --- a/client/src/components/SearchBar/LocationInputField.tsx +++ b/client/src/components/SearchBar/LocationInputField.tsx @@ -1,8 +1,37 @@ import { Form } from 'react-bootstrap'; -import { COORDINATE_PRECISION } from './constants.ts'; -import { Location } from '../../gql/graphql.ts'; +import { toString, parseLocation } from '../../util/locationConverter.ts'; +import { Location, TripQueryVariables } from '../../gql/graphql.ts'; +import { useCallback, useEffect, useState } from 'react'; + +interface Props { + id: string; + label: string; + tripQueryVariables: TripQueryVariables; + setTripQueryVariables: (tripQueryVariables: TripQueryVariables) => void; + locationFieldKey: 'from' | 'to'; +} + +export function LocationInputField({ id, label, tripQueryVariables, setTripQueryVariables, locationFieldKey }: Props) { + const [value, setValue] = useState(''); + + useEffect(() => { + const initialLocation: Location = tripQueryVariables[locationFieldKey]; + + setValue(toString(initialLocation) || ''); + }, [tripQueryVariables, locationFieldKey]); + + const onLocationChange = useCallback( + (value: string) => { + const newLocation = parseLocation(value) || {}; + + setTripQueryVariables({ + ...tripQueryVariables, + [locationFieldKey]: newLocation, + }); + }, + [tripQueryVariables, setTripQueryVariables, locationFieldKey], + ); -export function LocationInputField({ location, id, label }: { location: Location; id: string; label: string }) { return ( @@ -14,16 +43,13 @@ export function LocationInputField({ location, id, label }: { location: Location size="sm" placeholder="[Click in map]" className="input-medium" - // Intentionally empty for now, but needed because of - // https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable - onChange={() => {}} - value={ - location.coordinates - ? `${location.coordinates?.latitude.toPrecision( - COORDINATE_PRECISION, - )} ${location.coordinates?.longitude.toPrecision(COORDINATE_PRECISION)}` - : '' - } + onChange={(e) => { + setValue(e.target.value); + }} + onBlur={(event) => { + onLocationChange(event.target.value); + }} + value={value} /> ); diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index 73df12fe103..47781532179 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -38,9 +38,21 @@ export function SearchBar({ onRoute, tripQueryVariables, setTripQueryVariables, {showServerInfo && }
    - + - + diff --git a/client/src/hooks/useQuayCoordinateQuery.ts b/client/src/hooks/useQuayCoordinateQuery.ts new file mode 100644 index 00000000000..6d4655a6838 --- /dev/null +++ b/client/src/hooks/useQuayCoordinateQuery.ts @@ -0,0 +1,36 @@ +import { useEffect, useState } from 'react'; +import { request } from 'graphql-request'; +import { Location, QueryType } from '../gql/graphql.ts'; +import { getApiUrl } from '../util/getApiUrl.ts'; +import { graphql } from '../gql'; + +const query = graphql(` + query quayCoordinate($id: String!) { + quay(id: $id) { + latitude + longitude + } + } +`); + +export const useQuayCoordinateQuery = (location: Location) => { + const [data, setData] = useState(null); + + useEffect(() => { + const fetchData = async () => { + if (location.place) { + const variables = { id: location.place }; + try { + setData((await request(getApiUrl(), query, variables)) as QueryType); + } catch (e) { + console.error('Error at useQuayCoordinateQuery', e); + } + } else { + setData(null); + } + }; + fetchData(); + }, [location]); + + return data?.quay; +}; diff --git a/client/src/hooks/useTripQuery.ts b/client/src/hooks/useTripQuery.ts index 515cfae290a..f1f8c859bd0 100644 --- a/client/src/hooks/useTripQuery.ts +++ b/client/src/hooks/useTripQuery.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { request } from 'graphql-request'; -import { QueryType, TripQueryVariables } from '../gql/graphql.ts'; +import { Location, QueryType, TripQueryVariables } from '../gql/graphql.ts'; import { getApiUrl } from '../util/getApiUrl.ts'; import { query } from '../static/query/tripQuery.tsx'; @@ -22,10 +22,14 @@ export const useTripQuery: TripQueryHook = (variables) => { } else { if (variables) { setLoading(true); - if (pageCursor) { - setData((await request(getApiUrl(), query, { ...variables, pageCursor })) as QueryType); - } else { - setData((await request(getApiUrl(), query, variables)) as QueryType); + try { + if (pageCursor) { + setData((await request(getApiUrl(), query, { ...variables, pageCursor })) as QueryType); + } else { + setData((await request(getApiUrl(), query, variables)) as QueryType); + } + } catch (e) { + console.error('Error at useTripQuery', e); } setLoading(false); } else { @@ -37,10 +41,14 @@ export const useTripQuery: TripQueryHook = (variables) => { ); useEffect(() => { - if (variables?.from.coordinates && variables?.to.coordinates) { + if (validLocation(variables?.from) && validLocation(variables?.to)) { callback(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [variables?.from, variables?.to]); return [data, loading, callback]; }; + +function validLocation(location: Location | undefined) { + return location && (location.coordinates || location.place); +} diff --git a/client/src/util/getColorForLeg.ts b/client/src/util/getColorForLeg.ts index ecb8cbc1676..64b5fc7776f 100644 --- a/client/src/util/getColorForLeg.ts +++ b/client/src/util/getColorForLeg.ts @@ -8,7 +8,7 @@ const getColorForMode = function (mode: Mode) { if (mode === Mode.Rail) return '#86BF8B'; if (mode === Mode.Coach) return '#25642A'; if (mode === Mode.Metro) return '#D9B250'; - if (mode === Mode.Bus) return '#25642A'; + if (mode === Mode.Bus) return '#fe0000'; if (mode === Mode.Tram) return '#D9B250'; if (mode === Mode.Trolleybus) return '#25642A'; if (mode === Mode.Water) return '#81304C'; diff --git a/client/src/util/locationConverter.ts b/client/src/util/locationConverter.ts new file mode 100644 index 00000000000..952a36a1dc4 --- /dev/null +++ b/client/src/util/locationConverter.ts @@ -0,0 +1,37 @@ +import { COORDINATE_PRECISION } from '../components/SearchBar/constants.ts'; +import { Location } from '../gql/graphql.ts'; + +const DOUBLE_PATTERN = '-{0,1}\\d+(\\.\\d+){0,1}'; + +const LAT_LON_PATTERN = '(' + DOUBLE_PATTERN + ')(\\s+)(' + DOUBLE_PATTERN + ')'; + +export function parseLocation(value: string): Location | null { + const latLonMatch = value.match(LAT_LON_PATTERN); + + if (latLonMatch) { + return { + coordinates: { + latitude: +latLonMatch[1], + longitude: +latLonMatch[4], + }, + }; + } + + return { + place: value, + }; +} + +export function toString(location: Location): string | null { + if (location.coordinates) { + return `${location.coordinates?.latitude.toPrecision( + COORDINATE_PRECISION, + )} ${location.coordinates?.longitude.toPrecision(COORDINATE_PRECISION)}`; + } + + if (location.place) { + return location.place; + } + + return null; +} diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 6f4b3f9d8b4..17a54ede4ae 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -31,6 +31,29 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove reading agency and route brandingUrl from GTFS data [#6183](https://github.com/opentripplanner/OpenTripPlanner/pull/6183) - Fix NullPointerException when searching backwards with a frequency-based trip [#6211](https://github.com/opentripplanner/OpenTripPlanner/pull/6211) - Combine two multi-criteria searches in Raptor [#6182](https://github.com/opentripplanner/OpenTripPlanner/pull/6182) +- Implement alert node query in GTFS GraphQL API [#6225](https://github.com/opentripplanner/OpenTripPlanner/pull/6225) +- Fix hop geometries when one pattern is replaced by another with different number of stops [#6136](https://github.com/opentripplanner/OpenTripPlanner/pull/6136) +- Distinct coach from bus when reading in GTFS data and in GTFS GraphQL API [#6171](https://github.com/opentripplanner/OpenTripPlanner/pull/6171) +- Add provider of updates as a dimension to metrics. [#6199](https://github.com/opentripplanner/OpenTripPlanner/pull/6199) +- Return empty list if there is no siriUrls in situations/infoLinks [#6232](https://github.com/opentripplanner/OpenTripPlanner/pull/6232) +- OSM area processing obeys tag mapping [#6164](https://github.com/opentripplanner/OpenTripPlanner/pull/6164) +- Add `via` to GTFS GraphQL API [#5958](https://github.com/opentripplanner/OpenTripPlanner/pull/5958) +- Deprecate old alert translations in the GTFS API and add language param to a few alert fields [#6216](https://github.com/opentripplanner/OpenTripPlanner/pull/6216) +- add stopPositionInPattern in Stoptime in GTFS GraphQL API [#6204](https://github.com/opentripplanner/OpenTripPlanner/pull/6204) +- Fix parsing of wheelchair accessible parking, add wheelchair debug layer [#6229](https://github.com/opentripplanner/OpenTripPlanner/pull/6229) +- Add previousLegs into GTFS GraphQL API [#6142](https://github.com/opentripplanner/OpenTripPlanner/pull/6142) +- Fix stop index filtering on ServiceJourney Transmodel GraphQL API [#6251](https://github.com/opentripplanner/OpenTripPlanner/pull/6251) +- Fix rental searches when destination is in a no-drop-off zone [#6233](https://github.com/opentripplanner/OpenTripPlanner/pull/6233) +- Include empty rail stops in transfers [#6208](https://github.com/opentripplanner/OpenTripPlanner/pull/6208) +- Relax rejection of GTFS flex trips that also contain continuous stopping [#6231](https://github.com/opentripplanner/OpenTripPlanner/pull/6231) +- Remove legacy bike access mapping [#6248](https://github.com/opentripplanner/OpenTripPlanner/pull/6248) +- Filter import of rental data by pickup type [#6240](https://github.com/opentripplanner/OpenTripPlanner/pull/6240) +- Apply stricter motor vehicle nothrough traffic rules in Finland [#6254](https://github.com/opentripplanner/OpenTripPlanner/pull/6254) +- Add `vehicleRentalsByBbox` query to GTFS GraphQL API [#6186](https://github.com/opentripplanner/OpenTripPlanner/pull/6186) +- Improve performance of speculative rental vehicle use in reverse search [#6260](https://github.com/opentripplanner/OpenTripPlanner/pull/6260) +- Fix problem with relaxed-generalized-cost-at-destination [#6255](https://github.com/opentripplanner/OpenTripPlanner/pull/6255) +- Reject SIRI-ET updates with empty StopPointRefs [#6266](https://github.com/opentripplanner/OpenTripPlanner/pull/6266) +- Allow GTFS fuzzy trip matching even when trip descriptor has an id [#6250](https://github.com/opentripplanner/OpenTripPlanner/pull/6250) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index 8d94e332e40..f80966b8cb3 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -219,39 +219,40 @@ Here is a list of all features which can be toggled on/off and their default val -| Feature | Description | Enabled by default | Sandbox | -|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| -| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | -| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | -| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | -| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | -| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | -| `ExtraTransferLegOnSameStop` | Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated. | | | -| `FloatingBike` | Enable floating bike routing. | ✓️ | | -| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | -| `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | -| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | -| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | -| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | -| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | -| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | -| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | -| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | -| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | -| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | -| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | -| `FlexRouting` | Enable FLEX routing. | | ✓️ | -| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | -| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | | ✓️ | -| `MultiCriteriaGroupMaxFilter` | Keep the best itinerary with respect to each criteria used in the transit-routing search. For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group (transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default for now, until this feature is well tested. | | | -| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | -| `ReportApi` | Enable the report API. | | ✓️ | -| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | -| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | -| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | -| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | -| `Sorlandsbanen` | Include train Sørlandsbanen in results when searching in south of Norway. Only relevant in Norway. | | ✓️ | -| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | +| Feature | Description | Enabled by default | Sandbox | +|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| +| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | +| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | +| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | +| `IncludeEmptyRailStopsInTransfers` | Turning this on guarantees that Rail stops without scheduled departures still get included when generating transfers using `ConsiderPatternsForDirectTransfers`. It is common for stops to be assign at real-time for Rail. Turning this on will help to avoid dropping transfers which are needed, when the stop is in use later. Turning this on, if ConsiderPatternsForDirectTransfers is off has no effect. | | | +| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | +| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | +| `ExtraTransferLegOnSameStop` | Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated. | | | +| `FloatingBike` | Enable floating bike routing. | ✓️ | | +| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | +| `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | +| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | +| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | +| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | +| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | +| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | +| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | +| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | +| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | +| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | +| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | +| `FlexRouting` | Enable FLEX routing. | | ✓️ | +| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | +| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | | ✓️ | +| `MultiCriteriaGroupMaxFilter` | Keep the best itinerary with respect to each criteria used in the transit-routing search. For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group (transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default for now, until this feature is well tested. | | | +| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | +| `ReportApi` | Enable the report API. | | ✓️ | +| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | +| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | +| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | +| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | +| `Sorlandsbanen` | Include train Sørlandsbanen in results when searching in south of Norway. Only relevant in Norway. | | ✓️ | +| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | diff --git a/doc/user/IslandPruning.md b/doc/user/IslandPruning.md index c33d31dd010..6570d36041a 100644 --- a/doc/user/IslandPruning.md +++ b/doc/user/IslandPruning.md @@ -38,12 +38,12 @@ to have the same access properties. ![](images/nothruisland.png) *Some regular (gray colored) streets are blocked behind access restricted (red colored) connections. Walk routing to them fails because it would be considered as pass through traffic. -The image on the right shows that pruning added walk nothrough restricition to those streets, and routing works again.* +The image on the right shows that pruning added walk nothrough restriction to those streets, and routing works again.* ## Pruning algorithm Pruning analyses the three traverse modes - walk, bike and car - separately. For example, a resting area by a motorway may include some walking paths, but the only way to get there is -to use car. Therefore, it represents a disconnected 'island' when considering the walk mode. Pruning does not erase disconnected graph geometry as long as it +to use a car. Therefore, it represents a disconnected 'island' when considering the walk mode. Pruning does not erase disconnected graph geometry as long as it can be reached using any of the traverse modes. Instead, pruning removes traversal permission for each disconnected mode from the island. Pruning uses four parameters and some heuristics to decide if a disconnected sub graph is a real island to be retained, or a harmful data error: diff --git a/doc/user/UpdaterConfig.md b/doc/user/UpdaterConfig.md index cc5e1d75901..f61cce12f54 100644 --- a/doc/user/UpdaterConfig.md +++ b/doc/user/UpdaterConfig.md @@ -313,18 +313,19 @@ GBFS form factors: -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|---------------------------------------------------------------------------------------|:---------------:|---------------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "vehicle-rental" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [allowKeepingRentedVehicleAtDestination](#u_1_allowKeepingRentedVehicleAtDestination) | `boolean` | If a vehicle should be allowed to be kept at the end of a station-based rental. | *Optional* | `false` | 2.1 | -| frequency | `duration` | How often the data should be updated. | *Optional* | `"PT1M"` | 1.5 | -| [geofencingZones](#u_1_geofencingZones) | `boolean` | Compute rental restrictions based on GBFS 2.2 geofencing zones. | *Optional* | `false` | 2.3 | -| language | `string` | TODO | *Optional* | | 2.1 | -| [network](#u_1_network) | `string` | The name of the network to override the one derived from the source data. | *Optional* | | 1.5 | -| overloadingAllowed | `boolean` | Allow leaving vehicles at a station even though there are no free slots. | *Optional* | `false` | 2.2 | -| [sourceType](#u_1_sourceType) | `enum` | What source of vehicle rental updater to use. | *Required* | | 1.5 | -| url | `string` | The URL to download the data from. | *Required* | | 1.5 | -| [headers](#u_1_headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 1.5 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|---------------------------------------------------------------------------------------|:---------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "vehicle-rental" | `enum` | The type of the updater. | *Required* | | 1.5 | +| [allowKeepingRentedVehicleAtDestination](#u_1_allowKeepingRentedVehicleAtDestination) | `boolean` | If a vehicle should be allowed to be kept at the end of a station-based rental. | *Optional* | `false` | 2.1 | +| frequency | `duration` | How often the data should be updated. | *Optional* | `"PT1M"` | 1.5 | +| [geofencingZones](#u_1_geofencingZones) | `boolean` | Compute rental restrictions based on GBFS 2.2 geofencing zones. | *Optional* | `false` | 2.3 | +| language | `string` | TODO | *Optional* | | 2.1 | +| [network](#u_1_network) | `string` | The name of the network to override the one derived from the source data. | *Optional* | | 1.5 | +| overloadingAllowed | `boolean` | Allow leaving vehicles at a station even though there are no free slots. | *Optional* | `false` | 2.2 | +| [sourceType](#u_1_sourceType) | `enum` | What source of vehicle rental updater to use. | *Required* | | 1.5 | +| url | `string` | The URL to download the data from. | *Required* | | 1.5 | +| [headers](#u_1_headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 1.5 | +| [rentalPickupTypes](#u_1_rentalPickupTypes) | `enum set` | This is temporary and will be removed in a future version of OTP. Use this to specify the type of rental data that is allowed to be read from the data source. | *Optional* | | 2.7 | ##### Parameter details @@ -383,6 +384,18 @@ What source of vehicle rental updater to use. HTTP headers to add to the request. Any header key, value can be inserted. +

    rentalPickupTypes

    + +**Since version:** `2.7` ∙ **Type:** `enum set` ∙ **Cardinality:** `Optional` +**Path:** /updaters/[1] +**Enum values:** `station` | `free-floating` + +This is temporary and will be removed in a future version of OTP. Use this to specify the type of rental data that is allowed to be read from the data source. + + - `station` Stations are imported. + - `free-floating` Free-floating vehicles are imported. + + ##### Example configuration diff --git a/doc/user/osm/Default.md b/doc/user/osm/OsmTag.md similarity index 100% rename from doc/user/osm/Default.md rename to doc/user/osm/OsmTag.md diff --git a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md index 58d05d5490e..5232696ad9b 100644 --- a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md +++ b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md @@ -35,6 +35,7 @@ of the `router-config.json`. | feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.1 | | fuzzyTripMatching | `boolean` | If the trips should be matched fuzzily. | *Optional* | `false` | 2.1 | | [initialGetDataTimeout](#u__12__initialGetDataTimeout) | `duration` | Timeout for retrieving the recent history of SIRI-ET messages. | *Optional* | `"PT30S"` | 2.1 | +| producerMetrics | `boolean` | If failure, success, and warning metrics should be collected per producer. | *Optional* | `false` | 2.7 | | [reconnectPeriod](#u__12__reconnectPeriod) | `duration` | Wait this amount of time before trying to reconnect to the PubSub subscription. | *Optional* | `"PT30S"` | 2.1 | | [subscriptionProjectName](#u__12__subscriptionProjectName) | `string` | The Google Cloud project that hosts the PubSub subscription. | *Required* | | 2.1 | | topicName | `string` | The name of the PubSub topic that publishes the updates. | *Required* | | 2.1 | diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index f6c4c3f999f..28f2f9a85db 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -35,6 +35,7 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.0 | | fuzzyTripMatching | `boolean` | If the fuzzy trip matcher should be used to match trips. | *Optional* | `false` | 2.0 | | previewInterval | `duration` | TODO | *Optional* | | 2.0 | +| producerMetrics | `boolean` | If failure, success, and warning metrics should be collected per producer. | *Optional* | `false` | 2.7 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | | [url](#u__8__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | diff --git a/mkdocs.yml b/mkdocs.yml index 15e73773d78..b40f77ff3c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -74,7 +74,7 @@ nav: - Introduction: 'Configuration.md' - Build: 'BuildConfiguration.md' - OSM Tag Mapping: - - Default: 'osm/Default.md' + - Default: 'osm/OsmTag.md' - Finland: 'osm/Finland.md' - Germany: 'osm/Germany.md' - Norway: 'osm/Norway.md' diff --git a/pom.xml b/pom.xml index 284153b6486..b520f3cad63 100644 --- a/pom.xml +++ b/pom.xml @@ -58,14 +58,14 @@ - 170 + 172 32.0 2.52 2.18.1 3.1.9 - 5.11.2 - 1.13.5 + 5.11.3 + 1.13.7 5.6.0 1.5.12 9.12.0 @@ -97,6 +97,7 @@ application gtfs-realtime-protobuf utils + raptor @@ -173,7 +174,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.1 + 3.5.2 me.fabriciorby @@ -221,8 +222,6 @@ --add-opens java.base/sun.invoke.util=ALL-UNNAMED --add-opens java.xml/org.xml.sax.helpers=ALL-UNNAMED - - true plain true @@ -236,6 +235,7 @@ true true false + UNICODE diff --git a/raptor/pom.xml b/raptor/pom.xml new file mode 100644 index 00000000000..d2d93e36f9d --- /dev/null +++ b/raptor/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + org.opentripplanner + otp-root + 2.7.0-SNAPSHOT + + + otp-raptor + OpenTripPlanner - Raptor + + + + + + ${project.groupId} + otp-utils + ${project.version} + + + + + + + com.google.code.findbugs + jsr305 + + + net.sf.trove4j + trove4j + + + + org.slf4j + slf4j-api + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + com.tngtech.archunit + archunit + test + + + diff --git a/application/src/main/java/org/opentripplanner/raptor/RaptorService.java b/raptor/src/main/java/org/opentripplanner/raptor/RaptorService.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/RaptorService.java rename to raptor/src/main/java/org/opentripplanner/raptor/RaptorService.java diff --git a/application/src/main/java/org/opentripplanner/raptor/RaptorTimeLine.svg b/raptor/src/main/java/org/opentripplanner/raptor/RaptorTimeLine.svg similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/RaptorTimeLine.svg rename to raptor/src/main/java/org/opentripplanner/raptor/RaptorTimeLine.svg diff --git a/application/src/main/java/org/opentripplanner/raptor/api/debug/DebugEvent.java b/raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugEvent.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/debug/DebugEvent.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugEvent.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/debug/DebugLogger.java b/raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugLogger.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/debug/DebugLogger.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugLogger.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/debug/DebugTopic.java b/raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugTopic.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/debug/DebugTopic.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/debug/DebugTopic.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/debug/RaptorTimers.java b/raptor/src/main/java/org/opentripplanner/raptor/api/debug/RaptorTimers.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/debug/RaptorTimers.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/debug/RaptorTimers.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/AbstractAccessEgressDecorator.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/AbstractAccessEgressDecorator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/AbstractAccessEgressDecorator.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/AbstractAccessEgressDecorator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/DominanceFunction.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/DominanceFunction.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/DominanceFunction.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/DominanceFunction.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunction.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunction.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunction.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunction.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/PathLegType.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/PathLegType.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/PathLegType.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/PathLegType.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorAccessEgress.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorAccessEgress.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorAccessEgress.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorAccessEgress.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstants.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstants.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstants.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstants.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstrainedTransfer.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstrainedTransfer.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstrainedTransfer.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorConstrainedTransfer.java diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverter.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorCostConverter.java similarity index 86% rename from application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverter.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorCostConverter.java index 210197f107b..3801c948e3e 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverter.java +++ b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorCostConverter.java @@ -1,14 +1,14 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; +package org.opentripplanner.raptor.api.model; import java.time.Duration; -import org.opentripplanner.raptor.api.model.RaptorValueFormatter; /** - * Convert Raptor internal cost to OTP domain model cost, and back. + * Convert Raptor internal cost to OTP domain model cost, and back. This is provided by the Raptor + * module as a utility, but not used in Raptor except in unit-tests. *

    - * Inside Raptor the a cost unit is 1/100 of a "transit second" using {@code int} as type. In the - * OTP internal domain the unit used for cost is one "transit second" with type {@code double}. Cost - * in raptor is calculated using ints to spped up the calculations and to save memory. + * Inside Raptor the cost unit is 1/100 of a "transit second" using {@code int} as type. In the + * OTP internal domain the unit used for cost is one "transit second" with type {@code double}. + * Cost in raptor is calculated using ints to speed up the calculations and to save memory. *

    * The reason for using 1/100 of a second resolution is that we want a cost factor of {@code 0.99} * to win over a cost factor of {@code 1.00}. diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorStopNameResolver.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorStopNameResolver.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorStopNameResolver.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorStopNameResolver.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransfer.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransfer.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransfer.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransfer.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransferConstraint.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransferConstraint.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransferConstraint.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTransferConstraint.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripSchedule.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripSchedule.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripSchedule.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripSchedule.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RaptorValueFormatter.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorValueFormatter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RaptorValueFormatter.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RaptorValueFormatter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/RelaxFunction.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/RelaxFunction.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/RelaxFunction.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/RelaxFunction.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/SearchDirection.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/SearchDirection.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/SearchDirection.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/SearchDirection.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/model/TransitArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/api/model/TransitArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/model/TransitArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/model/TransitArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/EgressPathLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/EgressPathLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/EgressPathLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/EgressPathLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/PathLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/PathLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/PathLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/PathLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/PathStringBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/PathStringBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/PathStringBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/PathStringBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/RaptorPath.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/RaptorPath.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/RaptorPath.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/RaptorPath.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/TransferPathLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/TransferPathLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/TransferPathLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/TransferPathLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/path/TransitPathLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/api/path/TransitPathLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/path/TransitPathLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/path/TransitPathLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/DebugRequest.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/DebugRequest.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/DebugRequest.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/DebugRequest.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/DebugRequestBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/DebugRequestBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/DebugRequestBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/DebugRequestBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/DynamicSearchWindowCoefficients.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/DynamicSearchWindowCoefficients.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/DynamicSearchWindowCoefficients.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/DynamicSearchWindowCoefficients.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/Optimization.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/Optimization.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/Optimization.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/Optimization.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/PassThroughPoint.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorEnvironment.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorEnvironment.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorEnvironment.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorEnvironment.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorProfile.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorProfile.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorProfile.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorProfile.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequest.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequest.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequest.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequest.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequestBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequestBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequestBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorRequestBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorTuningParameters.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorTuningParameters.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorTuningParameters.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorTuningParameters.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaConnection.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaConnection.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaConnection.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaConnection.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaLocation.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaLocation.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaLocation.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/RaptorViaLocation.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/SearchParams.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/SearchParams.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/SearchParams.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/SearchParams.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/request/SearchParamsBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/api/request/SearchParamsBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/request/SearchParamsBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/request/SearchParamsBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/response/RaptorResponse.java b/raptor/src/main/java/org/opentripplanner/raptor/api/response/RaptorResponse.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/response/RaptorResponse.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/response/RaptorResponse.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/response/StopArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/api/response/StopArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/response/StopArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/response/StopArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/view/AccessPathView.java b/raptor/src/main/java/org/opentripplanner/raptor/api/view/AccessPathView.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/view/AccessPathView.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/view/AccessPathView.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/view/ArrivalView.java b/raptor/src/main/java/org/opentripplanner/raptor/api/view/ArrivalView.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/view/ArrivalView.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/view/ArrivalView.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/view/EgressPathView.java b/raptor/src/main/java/org/opentripplanner/raptor/api/view/EgressPathView.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/view/EgressPathView.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/view/EgressPathView.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/view/PatternRideView.java b/raptor/src/main/java/org/opentripplanner/raptor/api/view/PatternRideView.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/view/PatternRideView.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/view/PatternRideView.java diff --git a/application/src/main/java/org/opentripplanner/raptor/api/view/TransitPathView.java b/raptor/src/main/java/org/opentripplanner/raptor/api/view/TransitPathView.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/api/view/TransitPathView.java rename to raptor/src/main/java/org/opentripplanner/raptor/api/view/TransitPathView.java diff --git a/application/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java b/raptor/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java similarity index 99% rename from application/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java rename to raptor/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java index ed10d0d715c..66d72900889 100644 --- a/application/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java +++ b/raptor/src/main/java/org/opentripplanner/raptor/configure/RaptorConfig.java @@ -90,7 +90,8 @@ public RaptorRouter createRangeRaptorWithMcWorker( mainSearch, alternativeSearch, extraMcSearch.merger(), - threadPool() + threadPool(), + environment::mapInterruptedException ); } diff --git a/application/src/main/java/org/opentripplanner/raptor/package.md b/raptor/src/main/java/org/opentripplanner/raptor/package.md similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/package.md rename to raptor/src/main/java/org/opentripplanner/raptor/package.md diff --git a/application/src/main/java/org/opentripplanner/raptor/path/Path.java b/raptor/src/main/java/org/opentripplanner/raptor/path/Path.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/path/Path.java rename to raptor/src/main/java/org/opentripplanner/raptor/path/Path.java diff --git a/application/src/main/java/org/opentripplanner/raptor/path/PathBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/path/PathBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/path/PathBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/path/PathBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/path/PathBuilderLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/path/PathBuilderLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/path/PathBuilderLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/path/PathBuilderLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/CompositeResult.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/CompositeResult.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/CompositeResult.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/CompositeResult.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java similarity index 86% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java index a4ddf36347f..1a16c6f0527 100644 --- a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java +++ b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/ConcurrentCompositeRaptorRouter.java @@ -4,8 +4,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.function.BiFunction; +import java.util.function.Function; import javax.annotation.Nullable; -import org.opentripplanner.framework.application.OTPRequestTimeoutException; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; import org.opentripplanner.raptor.rangeraptor.internalapi.RaptorRouter; @@ -26,16 +26,21 @@ public class ConcurrentCompositeRaptorRouter @Nullable private final ExecutorService executorService; + @Nullable + private final Function mapInterruptedException; + public ConcurrentCompositeRaptorRouter( RaptorRouter mainWorker, RaptorRouter alternativeWorker, BiFunction>, Collection>, Collection>> merger, - @Nullable ExecutorService executorService + @Nullable ExecutorService executorService, + @Nullable Function mapInterruptedException ) { this.mainWorker = mainWorker; this.alternativeWorker = alternativeWorker; this.merger = merger; this.executorService = executorService; + this.mapInterruptedException = mapInterruptedException; } @Override @@ -59,7 +64,7 @@ public RaptorRouterResult route() { mainResultFuture.cancel(true); alternativeResultFuture.cancel(true); - throw new OTPRequestTimeoutException(); + throw mapInterruptedException.apply(e); } catch (ExecutionException e) { throw (e.getCause() instanceof RuntimeException re) ? re : new RuntimeException(e); } diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/DefaultRangeRaptorWorker.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/DefaultRangeRaptorWorker.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/DefaultRangeRaptorWorker.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/DefaultRangeRaptorWorker.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptor.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptor.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptor.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptor.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptorWorkerComposite.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptorWorkerComposite.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptorWorkerComposite.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptorWorkerComposite.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContext.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContext.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContext.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContext.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextBuilder.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextBuilder.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextBuilder.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextBuilder.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextViaLeg.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextViaLeg.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextViaLeg.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextViaLeg.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/AbstractDebugHandlerAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/AbstractDebugHandlerAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/AbstractDebugHandlerAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/AbstractDebugHandlerAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPathAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPathAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPathAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPathAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPatternRideAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPatternRideAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPatternRideAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerPatternRideAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerStopArrivalAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerStopArrivalAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerStopArrivalAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/DebugHandlerStopArrivalAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/ParetoSetDebugHandlerAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/ParetoSetDebugHandlerAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/ParetoSetDebugHandlerAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/debug/ParetoSetDebugHandlerAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/DebugHandler.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/DebugHandler.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/DebugHandler.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/DebugHandler.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/HeuristicAtStop.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/HeuristicAtStop.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/HeuristicAtStop.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/HeuristicAtStop.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/Heuristics.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/Heuristics.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/Heuristics.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/Heuristics.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetCost.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetCost.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetCost.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetCost.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetTime.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetTime.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetTime.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/ParetoSetTime.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RangeRaptorWorker.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RangeRaptorWorker.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RangeRaptorWorker.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RangeRaptorWorker.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouterResult.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouterResult.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouterResult.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorRouterResult.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorWorkerState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorWorkerState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorWorkerState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RaptorWorkerState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RoutingStrategy.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RoutingStrategy.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RoutingStrategy.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/RoutingStrategy.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SingleCriteriaStopArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SingleCriteriaStopArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SingleCriteriaStopArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SingleCriteriaStopArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SlackProvider.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SlackProvider.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SlackProvider.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/SlackProvider.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/WorkerLifeCycle.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/WorkerLifeCycle.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/WorkerLifeCycle.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/WorkerLifeCycle.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleEventPublisher.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleEventPublisher.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleEventPublisher.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleEventPublisher.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleSubscriptions.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleSubscriptions.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleSubscriptions.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/lifecycle/LifeCycleSubscriptions.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/CalculateTransferToDestination.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/CalculateTransferToDestination.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/CalculateTransferToDestination.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/CalculateTransferToDestination.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/DebugStopArrivalsStatistics.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/DebugStopArrivalsStatistics.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/DebugStopArrivalsStatistics.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/DebugStopArrivalsStatistics.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRangeRaptorWorkerState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRangeRaptorWorkerState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRangeRaptorWorkerState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRangeRaptorWorkerState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRaptorRouterResult.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRaptorRouterResult.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRaptorRouterResult.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McRaptorRouterResult.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McStopArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McStopArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McStopArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/McStopArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/MultiCriteriaRoutingStrategy.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/MultiCriteriaRoutingStrategy.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/MultiCriteriaRoutingStrategy.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/MultiCriteriaRoutingStrategy.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalParetoSet.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalParetoSet.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalParetoSet.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalParetoSet.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ViaConnectionStopArrivalEventListener.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ViaConnectionStopArrivalEventListener.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ViaConnectionStopArrivalEventListener.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ViaConnectionStopArrivalEventListener.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AbstractStopArrivalC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AbstractStopArrivalC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AbstractStopArrivalC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AbstractStopArrivalC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/heuristic/HeuristicsProvider.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/heuristic/HeuristicsProvider.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/heuristic/HeuristicsProvider.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/heuristic/HeuristicsProvider.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsService.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsService.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsService.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsService.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRide.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRide.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRide.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRide.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRideFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRideFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRideFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/PatternRideFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PassThroughRideFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PassThroughRideFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PassThroughRideFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PassThroughRideFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/BoardAndAlightTimeSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/BoardAndAlightTimeSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/BoardAndAlightTimeSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/BoardAndAlightTimeSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrival.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrival.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrival.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrival.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalPaths.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalPaths.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalPaths.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalPaths.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ForwardPathMapper.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ForwardPathMapper.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ForwardPathMapper.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ForwardPathMapper.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathMapper.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathMapper.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathMapper.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathMapper.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ReversePathMapper.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ReversePathMapper.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ReversePathMapper.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/ReversePathMapper.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/configure/PathConfig.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/configure/PathConfig.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/path/configure/PathConfig.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/path/configure/PathConfig.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/ArrivalTimeRoutingStrategy.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/ArrivalTimeRoutingStrategy.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/ArrivalTimeRoutingStrategy.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/ArrivalTimeRoutingStrategy.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/MinTravelDurationRoutingStrategy.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/MinTravelDurationRoutingStrategy.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/MinTravelDurationRoutingStrategy.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/MinTravelDurationRoutingStrategy.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRangeRaptorWorkerState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRangeRaptorWorkerState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRangeRaptorWorkerState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRangeRaptorWorkerState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRaptorRouterResult.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRaptorRouterResult.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRaptorRouterResult.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdRaptorRouterResult.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdWorkerState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdWorkerState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdWorkerState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/StdWorkerState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimes.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimes.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimes.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimes.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimesOnlyStopArrivalsState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimesOnlyStopArrivalsState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimesOnlyStopArrivalsState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/BestTimesOnlyStopArrivalsState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheck.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheck.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheck.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheck.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleBestNumberOfTransfers.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleBestNumberOfTransfers.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleBestNumberOfTransfers.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleBestNumberOfTransfers.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/UnknownPathFactory.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/UnknownPathFactory.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/UnknownPathFactory.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/UnknownPathFactory.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/StdRangeRaptorConfig.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/StdRangeRaptorConfig.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/StdRangeRaptorConfig.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/StdRangeRaptorConfig.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/VerifyRequestIsValid.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/VerifyRequestIsValid.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/VerifyRequestIsValid.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/configure/VerifyRequestIsValid.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/DebugStopArrivalsState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/DebugStopArrivalsState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/DebugStopArrivalsState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/DebugStopArrivalsState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/StateDebugger.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/StateDebugger.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/StateDebugger.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/debug/StateDebugger.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/ArrivedAtDestinationCheck.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/ArrivedAtDestinationCheck.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/ArrivedAtDestinationCheck.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/ArrivedAtDestinationCheck.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/BestNumberOfTransfers.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/BestNumberOfTransfers.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/BestNumberOfTransfers.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/BestNumberOfTransfers.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/DestinationArrivalListener.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/DestinationArrivalListener.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/DestinationArrivalListener.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/DestinationArrivalListener.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/StopArrivalsState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/StopArrivalsState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/StopArrivalsState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/internalapi/StopArrivalsState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/AccessStopArrivalState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/AccessStopArrivalState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/AccessStopArrivalState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/AccessStopArrivalState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/DefaultStopArrivalState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/DefaultStopArrivalState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/DefaultStopArrivalState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/DefaultStopArrivalState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/EgressStopArrivalState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/EgressStopArrivalState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/EgressStopArrivalState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/EgressStopArrivalState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivalsState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivalsState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivalsState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StdStopArrivalsState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StopArrivalState.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StopArrivalState.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StopArrivalState.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/StopArrivalState.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/path/EgressArrivalToPathAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/path/EgressArrivalToPathAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/path/EgressArrivalToPathAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/path/EgressArrivalToPathAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Access.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Access.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Access.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Access.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopArrivalViewAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopArrivalViewAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopArrivalViewAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopArrivalViewAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transfer.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transfer.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transfer.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transfer.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transit.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transit.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transit.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/Transit.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/support/TimeBasedBoardingSupport.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/support/TimeBasedBoardingSupport.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/support/TimeBasedBoardingSupport.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/support/TimeBasedBoardingSupport.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenalty.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenalty.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenalty.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenalty.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenalty.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenalty.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenalty.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenalty.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorTransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorTransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorTransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorTransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTracker.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTracker.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTracker.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTracker.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapter.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapter.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapter.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapter.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TimeCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TimeCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TimeCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TimeCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TransitCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TransitCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TransitCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TransitCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ViaConnections.java b/raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ViaConnections.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ViaConnections.java rename to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/ViaConnections.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/DebugHeuristics.java b/raptor/src/main/java/org/opentripplanner/raptor/service/DebugHeuristics.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/DebugHeuristics.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/DebugHeuristics.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/DefaultStopArrivals.java b/raptor/src/main/java/org/opentripplanner/raptor/service/DefaultStopArrivals.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/DefaultStopArrivals.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/DefaultStopArrivals.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/DestinationNotReachedException.java b/raptor/src/main/java/org/opentripplanner/raptor/service/DestinationNotReachedException.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/DestinationNotReachedException.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/DestinationNotReachedException.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/HeuristicSearchTask.java b/raptor/src/main/java/org/opentripplanner/raptor/service/HeuristicSearchTask.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/HeuristicSearchTask.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/HeuristicSearchTask.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/HeuristicToRunResolver.java b/raptor/src/main/java/org/opentripplanner/raptor/service/HeuristicToRunResolver.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/HeuristicToRunResolver.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/HeuristicToRunResolver.java diff --git a/application/src/main/java/org/opentripplanner/raptor/service/RangeRaptorDynamicSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/service/RangeRaptorDynamicSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/service/RangeRaptorDynamicSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/service/RangeRaptorDynamicSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/BoardAndAlightTime.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/BoardAndAlightTime.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/BoardAndAlightTime.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/BoardAndAlightTime.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/DefaultSlackProvider.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/DefaultSlackProvider.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/DefaultSlackProvider.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/DefaultSlackProvider.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java similarity index 86% rename from application/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java index 07984903f2a..4c0b76d99f9 100644 --- a/application/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java +++ b/raptor/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java @@ -4,6 +4,7 @@ import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; +import org.opentripplanner.utils.time.TimeUtils; record EmptyBoardOrAlightEvent(int earliestBoardTime) implements RaptorBoardOrAlightEvent { @@ -44,4 +45,9 @@ public void boardWithFallback( ) { alternativeBoardingFallback.accept(this); } + + @Override + public String toString() { + return "EmptyBoardOrAlightEvent(" + TimeUtils.timeToStrLong(earliestBoardTime) + ")"; + } } diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/ExtraMcRouterSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/ExtraMcRouterSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/ExtraMcRouterSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/ExtraMcRouterSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/Flyweight.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/IntIterator.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/IntIterator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/IntIterator.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/IntIterator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorConstrainedBoardingSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorConstrainedBoardingSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorConstrainedBoardingSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorConstrainedBoardingSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorPathConstrainedTransferSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorPathConstrainedTransferSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorPathConstrainedTransferSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorPathConstrainedTransferSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorRoute.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorRoute.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorRoute.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorRoute.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorSlackProvider.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorSlackProvider.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorSlackProvider.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorSlackProvider.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorTimeTable.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTimeTable.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorTimeTable.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTimeTable.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/RaptorTripScheduleSearch.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTripScheduleSearch.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/RaptorTripScheduleSearch.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/RaptorTripScheduleSearch.java diff --git a/application/src/main/java/org/opentripplanner/raptor/spi/UnknownPath.java b/raptor/src/main/java/org/opentripplanner/raptor/spi/UnknownPath.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/spi/UnknownPath.java rename to raptor/src/main/java/org/opentripplanner/raptor/spi/UnknownPath.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/BitSetIterator.java b/raptor/src/main/java/org/opentripplanner/raptor/util/BitSetIterator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/BitSetIterator.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/BitSetIterator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/CompareIntArrays.java b/raptor/src/main/java/org/opentripplanner/raptor/util/CompareIntArrays.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/CompareIntArrays.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/CompareIntArrays.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/IntIterators.java b/raptor/src/main/java/org/opentripplanner/raptor/util/IntIterators.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/IntIterators.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/IntIterators.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/composite/CompositeUtil.java b/raptor/src/main/java/org/opentripplanner/raptor/util/composite/CompositeUtil.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/composite/CompositeUtil.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/composite/CompositeUtil.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoComparator.java b/raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoComparator.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoComparator.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoComparator.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSet.java b/raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSet.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSet.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSet.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListener.java b/raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListener.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListener.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListener.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerComposite.java b/raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerComposite.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerComposite.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerComposite.java diff --git a/application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarker.java b/raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarker.java similarity index 100% rename from application/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarker.java rename to raptor/src/main/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarker.java diff --git a/application/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java b/raptor/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java similarity index 84% rename from application/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java index 13f341279f7..3ada07c090b 100644 --- a/application/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/RaptorArchitectureTest.java @@ -1,21 +1,22 @@ package org.opentripplanner.raptor; import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices; -import static org.opentripplanner.OtpArchitectureModules.FRAMEWORK_UTILS; -import static org.opentripplanner.OtpArchitectureModules.GNU_TROVE; -import static org.opentripplanner.OtpArchitectureModules.OTP_ROOT; -import static org.opentripplanner.OtpArchitectureModules.RAPTOR_API; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.opentripplanner._support.arch.ArchComponent; -import org.opentripplanner._support.arch.Module; -import org.opentripplanner._support.arch.Package; +import org.opentripplanner.raptor._support.arch.ArchComponent; +import org.opentripplanner.raptor._support.arch.Module; +import org.opentripplanner.raptor._support.arch.Package; public class RaptorArchitectureTest { + private static final Package OTP_ROOT = Package.of("org.opentripplanner"); + private static final Package GNU_TROVE = Package.of("gnu.trove.."); + private static final Package OTP_UTILS = OTP_ROOT.subPackage("utils.."); + /* The Raptor module, all packages that other paths of OTP may use. */ private static final Package RAPTOR = OTP_ROOT.subPackage("raptor"); + private static final Package RAPTOR_API = RAPTOR.subPackage("api.."); private static final Package API = RAPTOR.subPackage("api"); private static final Package API_MODEL = API.subPackage("model"); private static final Package API_PATH = API.subPackage("path"); @@ -49,7 +50,7 @@ public class RaptorArchitectureTest { * Packages used by standard-range-raptor and multi-criteria-range-raptor. */ private static final Module RR_SHARED_PACKAGES = Module.of( - FRAMEWORK_UTILS, + OTP_UTILS, GNU_TROVE, RAPTOR_API, RAPTOR_SPI, @@ -64,37 +65,37 @@ public class RaptorArchitectureTest { @Test void enforcePackageDependenciesRaptorAPI() { - API_MODEL.dependsOn(FRAMEWORK_UTILS).verify(); - API_PATH.dependsOn(FRAMEWORK_UTILS, API_MODEL).verify(); - var debug = API.subPackage("debug").dependsOn(FRAMEWORK_UTILS).verify(); - var view = API.subPackage("view").dependsOn(FRAMEWORK_UTILS, API_MODEL).verify(); + API_MODEL.dependsOn(OTP_UTILS).verify(); + API_PATH.dependsOn(OTP_UTILS, API_MODEL).verify(); + var debug = API.subPackage("debug").dependsOn(OTP_UTILS).verify(); + var view = API.subPackage("view").dependsOn(OTP_UTILS, API_MODEL).verify(); var request = API .subPackage("request") - .dependsOn(FRAMEWORK_UTILS, debug, API_MODEL, API_PATH, view) + .dependsOn(OTP_UTILS, debug, API_MODEL, API_PATH, view) .verify(); - API.subPackage("response").dependsOn(FRAMEWORK_UTILS, API_MODEL, API_PATH, request).verify(); + API.subPackage("response").dependsOn(OTP_UTILS, API_MODEL, API_PATH, request).verify(); } @Test void enforcePackageDependenciesRaptorSPI() { - RAPTOR_SPI.dependsOn(FRAMEWORK_UTILS, API_MODEL, API_PATH).verify(); + RAPTOR_SPI.dependsOn(OTP_UTILS, API_MODEL, API_PATH).verify(); } @Test void enforcePackageDependenciesUtil() { - RAPTOR_UTIL.dependsOn(FRAMEWORK_UTILS, RAPTOR_SPI).verify(); + RAPTOR_UTIL.dependsOn(OTP_UTILS, RAPTOR_SPI).verify(); RAPTOR_UTIL_PARETO_SET.dependsOn(RAPTOR_UTIL_COMPOSITE).verify(); RAPTOR_UTIL_COMPOSITE.verify(); } @Test void enforcePackageDependenciesRaptorPath() { - RAPTOR_PATH.dependsOn(FRAMEWORK_UTILS, API_PATH, API_MODEL, RAPTOR_SPI, RR_TRANSIT).verify(); + RAPTOR_PATH.dependsOn(OTP_UTILS, API_PATH, API_MODEL, RAPTOR_SPI, RR_TRANSIT).verify(); } @Test void enforcePackageDependenciesInRangeRaptorSharedPackages() { - RR_INTERNAL_API.dependsOn(FRAMEWORK_UTILS, RAPTOR_API, RAPTOR_SPI).verify(); + RR_INTERNAL_API.dependsOn(OTP_UTILS, RAPTOR_API, RAPTOR_SPI).verify(); RR_DEBUG.dependsOn(RR_SHARED_PACKAGES).verify(); RR_LIFECYCLE.dependsOn(RR_SHARED_PACKAGES).verify(); RR_TRANSIT.dependsOn(RR_SHARED_PACKAGES, RR_DEBUG, RR_LIFECYCLE).verify(); @@ -200,7 +201,7 @@ void enforcePackageDependenciesInMultiCriteriaImplementation() { void enforcePackageDependenciesInRaptorService() { SERVICE .dependsOn( - FRAMEWORK_UTILS, + OTP_UTILS, RAPTOR_API, RAPTOR_SPI, RAPTOR_UTIL, @@ -216,6 +217,7 @@ void enforcePackageDependenciesInRaptorService() { void enforcePackageDependenciesInConfigure() { CONFIGURE .dependsOn( + OTP_UTILS, RAPTOR_API, RAPTOR_SPI, RANGE_RAPTOR, @@ -223,8 +225,7 @@ void enforcePackageDependenciesInConfigure() { RR_TRANSIT, RR_CONTEXT, RR_STD_CONFIGURE, - RR_MC_CONFIGURE, - FRAMEWORK_UTILS + RR_MC_CONFIGURE ) .verify(); } diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/api/PathUtils.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/api/PathUtils.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/api/PathUtils.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/api/PathUtils.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilder.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilder.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilder.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilder.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java similarity index 97% rename from application/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java index 9b37057cdc6..e5a48ecd289 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestPathBuilderTestRaptor.java @@ -2,9 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.model.transfer.TransferConstraint.REGULAR_TRANSFER; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; import static org.opentripplanner.raptor._data.transit.TestAccessEgress.walk; +import static org.opentripplanner.raptor.api.model.RaptorTransferConstraint.REGULAR_TRANSFER; import static org.opentripplanner.utils.time.DurationUtils.durationInSeconds; import static org.opentripplanner.utils.time.TimeUtils.time; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/api/TestRaptorPath.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestRaptorPath.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/api/TestRaptorPath.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/api/TestRaptorPath.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/multicriteria/ride/TestPatterRideBuilder.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/multicriteria/ride/TestPatterRideBuilder.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/multicriteria/ride/TestPatterRideBuilder.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/multicriteria/ride/TestPatterRideBuilder.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AbstractStopArrival.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AbstractStopArrival.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AbstractStopArrival.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AbstractStopArrival.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Access.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Access.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Access.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Access.java diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AccessAndEgressWithOpeningHoursPathTestCase.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AccessAndEgressWithOpeningHoursPathTestCase.java new file mode 100644 index 00000000000..89e13aa58ec --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/AccessAndEgressWithOpeningHoursPathTestCase.java @@ -0,0 +1,381 @@ +package org.opentripplanner.raptor._data.stoparrival; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.access; +import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.bus; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorValueFormatter.formatC1; +import static org.opentripplanner.utils.time.DurationUtils.durationInSeconds; +import static org.opentripplanner.utils.time.TimeUtils.time; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestCostCalculator; +import org.opentripplanner.raptor._data.transit.TestTransfer; +import org.opentripplanner.raptor._data.transit.TestTripPattern; +import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; +import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.raptor.api.view.ArrivalView; +import org.opentripplanner.raptor.rangeraptor.path.DestinationArrival; +import org.opentripplanner.raptor.spi.DefaultSlackProvider; +import org.opentripplanner.raptor.spi.RaptorSlackProvider; +import org.opentripplanner.utils.time.TimeUtils; + +/** + * This test case construct two Raptor paths for forward and reverse search, with and without + * opening hours for the flex access and egress. + *

    + * Case A with flex access and egress and one transit: + *

      + *
    1. Flex access
    2. + *
    3. Transit, BUS A
    4. + *
    5. Flex egress
    6. + *
    + *

    + * Case B with walking between transit and flex: + *

      + *
    1. Flex access
    2. + *
    3. Walk transfer
    4. + *
    5. Transit. BUS B
    6. + *
    7. Walk transfer
    8. + *
    9. Flex egress
    10. + *
    + */ +public class AccessAndEgressWithOpeningHoursPathTestCase implements RaptorTestConstants { + + private static final int ZERO = 0; + // The transit reluctance is ignored, any value should work + private static final int TRANSIT_RELUCTANCE_INDEX = -1; + public static final double WAIT_RELUCTANCE = 0.8; + public static final int BOARD_C1_SEC = 60; + public static final int TRANSFER_C1_SEC = 120; + // The C1_CALCULATOR is not under test, so we use it to calculate correct cost values. + public static final TestCostCalculator C1_CALCULATOR = new TestCostCalculator( + BOARD_C1_SEC, + TRANSFER_C1_SEC, + WAIT_RELUCTANCE, + null + ); + + public static final RaptorSlackProvider SLACK_PROVIDER = new DefaultSlackProvider( + TRANSFER_SLACK, + BOARD_SLACK, + ALIGHT_SLACK + ); + + // FLEX Access 5m tx 1 ~ A. Note! The actual times might get time-shifted. + public static final int ACCESS_DURATION = durationInSeconds("5m15s"); + public static final int ACCESS_C1 = toRaptorCost(600); + // Using transfer reluctance is incorrect, we should use the cost from the access path + public static final TestAccessEgress ACCESS = TestAccessEgress.flex( + STOP_A, + ACCESS_DURATION, + ONE_RIDE, + ACCESS_C1 + ); + // Alternative Flex access with restricted opening hours: 09:00 - 09:50 + public static final int ACCESS_OPEN = time("09:00"); + public static final int ACCESS_CLOSE = time("09:50"); + public static final TestAccessEgress ACCESS_W_OPENING_HOURS = ACCESS.openingHours( + ACCESS_OPEN, + ACCESS_CLOSE + ); + + // Transfers (A ~ Walk 1m ~ B) (Used in Case B only) + public static final int TX1_START = time("10:05:15"); + public static final int TX1_END = time("10:06:15"); + public static final int TX1_DURATION = TX1_END - TX1_START; + public static final RaptorTransfer TX1_TRANSFER = TestTransfer.transfer(STOP_B, TX1_DURATION); + public static final RaptorTransfer TX1_TRANSFER_REV = TestTransfer.transfer(STOP_A, TX1_DURATION); + public static final int TX1_C1 = TX1_TRANSFER.c1(); + + // Trip A (B ~ BUS L11 10:08 10:20 ~ C) + public static final int L1_START = time("10:08"); + public static final int L1_END = time("10:20"); + // The departure time with transfer_slack excluded + public static final int L1_STOP_ARR_TIME = L1_END + ALIGHT_SLACK; + public static final int L1_STOP_ARR_TIME_REV = L1_START - BOARD_SLACK; + + // Wait at least 1m45s (45s BOARD_SLACK and 60s TRANSFER_SLACK) + public static final int L1_TRANSIT_DURATION = L1_END - L1_START; + + // Transfers (C ~ Walk 2m ~ D) (Used in Case B only) + public static final int TX2_START = time("10:20:15"); + public static final int TX2_END = time("10:22:15"); + public static final int TX2_DURATION = TX2_END - TX2_START; + public static final RaptorTransfer TX2_TRANSFER = TestTransfer.transfer(STOP_D, TX2_DURATION); + public static final RaptorTransfer TX2_TRANSFER_REV = TestTransfer.transfer(STOP_C, TX2_DURATION); + public static final int TX2_C1 = TX2_TRANSFER.c1(); + + // Wait 15s (ALIGHT_SLACK) + // D ~ FLEX Egress 6m tx 1 . Note! The actual times might get time-shifted. + public static final int EGRESS_DURATION = durationInSeconds("6m"); + public static final int EGRESS_C1 = toRaptorCost(800); + // Using transfer reluctance is incorrect, we should use the cost from the egress path + public static final TestAccessEgress EGRESS = TestAccessEgress.flex( + STOP_D, + EGRESS_DURATION, + ONE_RIDE, + EGRESS_C1 + ); + public static final int EGRESS_OPENING = TimeUtils.time("10:30"); + public static final int EGRESS_CLOSING = TimeUtils.time("11:00"); + public static final TestAccessEgress EGRESS_W_OPENING_HOURS = EGRESS.openingHours( + EGRESS_OPENING, + EGRESS_CLOSING + ); + + public static final int EGRESS_C1_W_1M_SLACK = + EGRESS_C1 + toRaptorCost(TRANSFER_C1_SEC) + C1_CALCULATOR.waitCost(TRANSFER_SLACK); + public static final int EGRESS_C1_W_7M45S_SLACK = + EGRESS_C1_W_1M_SLACK + C1_CALCULATOR.waitCost(durationInSeconds("6m45s")); + public static final int EGRESS_C1_W_9M45S_SLACK = + EGRESS_C1_W_1M_SLACK + C1_CALCULATOR.waitCost(durationInSeconds("8m45s")); + + public static final String LINE_A = "A"; + public static final String LINE_B = "B"; + + public static final TestTripSchedule TRIP_A = TestTripSchedule + .schedule(TestTripPattern.pattern(LINE_A, STOP_A, STOP_D)) + .times(L1_START, L1_END) + .build(); + + public static final TestTripSchedule TRIP_B = TestTripSchedule + .schedule(TestTripPattern.pattern(LINE_B, STOP_B, STOP_C)) + .times(L1_START, L1_END) + .build(); + + public static final int L1_C1_EX_WAIT = C1_CALCULATOR.transitArrivalCost( + C1_CALCULATOR.boardingCostRegularTransfer(false, L1_START, STOP_B, L1_START), + ZERO, + L1_TRANSIT_DURATION, + TRIP_A, + STOP_C + ); + + private static final int TOT_C1_A = toRaptorCost(2564); + private static final int TOT_C1_W_OPENING_HOURS_A = toRaptorCost(3512); + private static final int TOT_C1_B = toRaptorCost(2924); + private static final int TOT_C1_W_OPENING_HOURS_B = toRaptorCost(3728); + // Wait before 12m45s + ALIGHT SLACK 15s + private static final int L1_C1_INC_WAIT_W_OPENING_HOURS_A = + L1_C1_EX_WAIT + C1_CALCULATOR.waitCost(durationInSeconds("13m")); + private static final int L1_C1_INC_WAIT_W_OPENING_HOURS_B = + L1_C1_EX_WAIT + C1_CALCULATOR.waitCost(durationInSeconds("12m")); + + /* TEST CASES WITH EXPECTED TO-STRING TEXTS */ + + public static DestinationArrival flexCaseAForwardSearch() { + return flexForwardSearch(ACCESS, EGRESS, LINE_A); + } + + public static String flexCaseAText() { + return String.format( + "Flex 5m15s 1x 10:01 10:06:15 %s ~ A 1m45s ~ " + + "BUS A 10:08 10:20 12m C₁996 ~ D 1m15s ~ " + + "Flex 6m 1x 10:21:15 10:27:15 %s " + + "[10:01 10:27:15 26m15s Tₓ2 %s]", + RaptorCostConverter.toString(ACCESS_C1), + RaptorCostConverter.toString(EGRESS_C1_W_1M_SLACK), + RaptorCostConverter.toString(TOT_C1_A) + ); + } + + public static DestinationArrival flexCaseBForwardSearch() { + return flexForwardSearch(ACCESS, EGRESS, LINE_B); + } + + public static String flexCaseBText() { + return String.format( + "Flex 5m15s 1x 10:00 10:05:15 %s ~ A 0s ~ " + + "Walk 1m 10:05:15 10:06:15 C₁120 ~ B 1m45s ~ " + + "BUS B 10:08 10:20 12m C₁996 ~ C 15s ~ " + + "Walk 2m 10:20:15 10:22:15 C₁240 ~ D 1m ~ " + + "Flex 6m 1x 10:23:15 10:29:15 %s" + + " [10:00 10:29:15 29m15s Tₓ2 %s]", + RaptorCostConverter.toString(ACCESS_C1), + RaptorCostConverter.toString(EGRESS_C1_W_1M_SLACK), + RaptorCostConverter.toString(TOT_C1_B) + ); + } + + public static DestinationArrival flexCaseAWithOpeningHoursForwardSearch() { + return flexForwardSearch(ACCESS_W_OPENING_HOURS, EGRESS_W_OPENING_HOURS, LINE_A); + } + + public static String flexCaseAWithOpeningHoursText() { + return String.format( + "Flex 5m15s 1x Open(9:00 9:50) 9:50 9:55:15 %s ~ A 12m45s ~ " + + "BUS A 10:08 10:20 12m %s ~ D 10m ~ " + + "Flex 6m 1x Open(10:30 11:00) 10:30 10:36 %s " + + "[9:50 10:36 46m Tₓ2 %s]", + formatC1(ACCESS_C1), + formatC1(L1_C1_INC_WAIT_W_OPENING_HOURS_A), + formatC1(EGRESS_C1_W_9M45S_SLACK), + formatC1(TOT_C1_W_OPENING_HOURS_A) + ); + } + + public static DestinationArrival flexCaseBWithOpeningHoursForwardSearch() { + return flexForwardSearch(ACCESS_W_OPENING_HOURS, EGRESS_W_OPENING_HOURS, LINE_B); + } + + public static String flexCaseBWithOpeningHoursText() { + return String.format( + "Flex 5m15s 1x Open(9:00 9:50) 9:50 9:55:15 %s ~ A 0s ~ " + + "Walk 1m 9:55:15 9:56:15 C₁120 ~ B 11m45s ~ " + + "BUS B 10:08 10:20 12m %s ~ C 15s ~ " + + "Walk 2m 10:20:15 10:22:15 C₁240 ~ D 7m45s ~ " + + "Flex 6m 1x Open(10:30 11:00) 10:30 10:36 %s" + + " [9:50 10:36 46m Tₓ2 %s]", + formatC1(ACCESS_C1), + formatC1(L1_C1_INC_WAIT_W_OPENING_HOURS_B), + formatC1(EGRESS_C1_W_7M45S_SLACK), + formatC1(TOT_C1_W_OPENING_HOURS_B) + ); + } + + public static DestinationArrival flexCaseAReverseSearch() { + return flexReverseSearch(ACCESS, EGRESS, LINE_A); + } + + public static DestinationArrival flexCaseBReverseSearch() { + return flexReverseSearch(ACCESS, EGRESS, LINE_B); + } + + public static DestinationArrival flexCaseAWithOpeningHoursReverseSearch() { + return flexReverseSearch(ACCESS_W_OPENING_HOURS, EGRESS_W_OPENING_HOURS, LINE_A); + } + + public static DestinationArrival flexCaseBWithOpeningHoursReverseSearch() { + return flexReverseSearch(ACCESS_W_OPENING_HOURS, EGRESS_W_OPENING_HOURS, LINE_B); + } + + @Test + public void testSetup() { + // Assert test data is configured correct + + // Assert all durations + assertEquals(TX1_END - TX1_START, TX1_DURATION); + assertEquals(L1_END - L1_START, L1_TRANSIT_DURATION); + assertEquals(TX2_END - TX2_START, TX2_DURATION); + + // Asset proper wait times + int txBoardSlack = TRANSFER_SLACK + BOARD_SLACK; + assertEquals(TX1_END + txBoardSlack, L1_START); + assertEquals(L1_END + ALIGHT_SLACK, TX2_START); + + // Assert cost + // The calculator is not under test here, so we assert everything is as expected + assertEquals(12000, TX1_C1); + assertEquals(90000, L1_C1_EX_WAIT); + assertEquals(24000, TX2_C1); + } + + /* PRIVATE METHODS */ + + private static DestinationArrival flexForwardSearch( + RaptorAccessEgress accessPath, + RaptorAccessEgress egressPath, + String line + ) { + int departureTime, arrivalTime, waitTime; + ArrivalView prevArrival; + + if (LINE_A.equals(line)) { + // The latest time the access can arrive is the same as the TX1 arrival time in case B + arrivalTime = accessPath.latestArrivalTime(TX1_END); + prevArrival = access(accessPath.stop(), arrivalTime, accessPath); + + int waitCost = costL1ForwardIncWait(prevArrival.arrivalTime()); + prevArrival = bus(2, STOP_D, L1_STOP_ARR_TIME, waitCost, 0, TRIP_A, prevArrival); + } else { + arrivalTime = accessPath.latestArrivalTime(TX1_START); + prevArrival = access(accessPath.stop(), arrivalTime, accessPath); + int timeShift = TX1_START - prevArrival.arrivalTime(); + + prevArrival = new Transfer(1, TX1_END - timeShift, TX1_TRANSFER, prevArrival); + + int waitCost = costL1ForwardIncWait(prevArrival.arrivalTime()); + prevArrival = bus(2, STOP_C, L1_STOP_ARR_TIME, waitCost, 0, TRIP_B, prevArrival); + + prevArrival = new Transfer(2, TX2_END, TX2_TRANSFER, prevArrival); + } + + // Egress + departureTime = prevArrival.arrivalTime() + TRANSFER_SLACK; + // Time-shift departure time + departureTime = egressPath.earliestDepartureTime(departureTime); + arrivalTime = departureTime + egressPath.durationInSeconds(); + waitTime = departureTime - prevArrival.arrivalTime(); + int additionalCost = + egressPath.c1() + toRaptorCost(waitTime * WAIT_RELUCTANCE + TRANSFER_C1_SEC); + + return new DestinationArrival<>( + egressPath, + prevArrival, + arrivalTime, + additionalCost, + RaptorConstants.NOT_SET + ); + } + + private static DestinationArrival flexReverseSearch( + RaptorAccessEgress accessPath, + RaptorAccessEgress egressPath, + String line + ) { + int departureTime, arrivalTime, cost; + ArrivalView prevArrival; + + if (LINE_A.equals(line)) { + arrivalTime = L1_END + ALIGHT_SLACK + TRANSFER_SLACK; + arrivalTime = egressPath.earliestDepartureTime(arrivalTime); + prevArrival = access(egressPath.stop(), arrivalTime, egressPath); + + cost = costL1ReverseIncWait(prevArrival.arrivalTime()); + prevArrival = bus(2, STOP_A, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_A, prevArrival); + } else { + arrivalTime = L1_END + ALIGHT_SLACK + TX2_DURATION + TRANSFER_SLACK; + arrivalTime = egressPath.earliestDepartureTime(arrivalTime); + prevArrival = access(egressPath.stop(), arrivalTime, egressPath); + arrivalTime = prevArrival.arrivalTime() - TX2_DURATION; + prevArrival = new Transfer(1, arrivalTime, TX2_TRANSFER_REV, prevArrival); + cost = costL1ReverseIncWait(prevArrival.arrivalTime()); + prevArrival = bus(2, STOP_B, L1_STOP_ARR_TIME_REV, cost, 0, TRIP_B, prevArrival); + arrivalTime = prevArrival.arrivalTime() - TX1_DURATION; + prevArrival = new Transfer(2, arrivalTime, TX1_TRANSFER_REV, prevArrival); + } + + // Access + departureTime = prevArrival.arrivalTime() - TRANSFER_SLACK; + // Time-shift departure time + departureTime = accessPath.latestArrivalTime(departureTime); + arrivalTime = departureTime - accessPath.durationInSeconds(); + int waitTime = prevArrival.arrivalTime() - departureTime; + int additionalCost = + accessPath.c1() + toRaptorCost(waitTime * WAIT_RELUCTANCE + TRANSFER_C1_SEC); + + return new DestinationArrival<>( + accessPath, + prevArrival, + arrivalTime, + additionalCost, + RaptorConstants.NOT_SET + ); + } + + private static int costL1ForwardIncWait(int prevArrivalTime) { + int waitTime = L1_START - prevArrivalTime + ALIGHT_SLACK; + return toRaptorCost(waitTime * WAIT_RELUCTANCE) + L1_C1_EX_WAIT; + } + + private static int costL1ReverseIncWait(int prevArrivalTime) { + int waitTime = (prevArrivalTime - L1_END) + BOARD_SLACK; + return toRaptorCost(waitTime * WAIT_RELUCTANCE) + L1_C1_EX_WAIT; + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java similarity index 96% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java index 88aa857f76a..5163140e40f 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/BasicPathTestCase.java @@ -1,14 +1,14 @@ package org.opentripplanner.raptor._data.stoparrival; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.model.transfer.TransferConstraint.REGULAR_TRANSFER; import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.access; import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.bus; import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.egress; import static org.opentripplanner.raptor._data.stoparrival.TestArrivals.transfer; import static org.opentripplanner.raptor._data.transit.TestAccessEgress.flexWithOnBoard; import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorTransferConstraint.REGULAR_TRANSFER; import static org.opentripplanner.utils.time.DurationUtils.durationToStr; import static org.opentripplanner.utils.time.TimeUtils.time; @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestCostCalculator; import org.opentripplanner.raptor._data.transit.TestTransfer; import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; @@ -34,7 +35,6 @@ import org.opentripplanner.raptor.rangeraptor.lifecycle.LifeCycleSubscriptions; import org.opentripplanner.raptor.rangeraptor.path.DestinationArrival; import org.opentripplanner.raptor.spi.RaptorCostCalculator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; /** * This class is used to create a journeys with stop arrivals. @@ -96,7 +96,7 @@ public class BasicPathTestCase implements RaptorTestConstants { /** Stop cost for stop NA, A, C, E .. H is zero(0), B: 30s, and D: 60s. ?=0, A=1 .. H=8 */ private static final int[] STOP_C1S = { 0, 0, 3_000, 0, 6_000, 0, 0, 0, 0, 0 }; - // Some times which should not have eny effect on tests + // These times should not have eny effect on tests private static final int VERY_EARLY = time("00:00"); private static final int VERY_LATE = time("23:59"); @@ -195,13 +195,11 @@ public class BasicPathTestCase implements RaptorTestConstants { public static final TestTripSchedule TRIP_1 = TestTripSchedule .schedule(pattern(LINE_11, STOP_A, STOP_B)) .times(L11_START, L11_END) - .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) .build(); public static final TestTripSchedule TRIP_2 = TestTripSchedule .schedule(pattern(LINE_21, STOP_C, STOP_D)) .times(L21_START, L21_END) - .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) .build(); public static final TestTripSchedule TRIP_3 = TestTripSchedule @@ -209,14 +207,12 @@ public class BasicPathTestCase implements RaptorTestConstants { // The early arrival and late departure should not have any effect on tests .arrivals(VERY_EARLY, L31_END) .departures(L31_START, VERY_LATE) - .transitReluctanceIndex(TRANSIT_RELUCTANCE_INDEX) .build(); - public static final RaptorCostCalculator C1_CALCULATOR = new DefaultCostCalculator<>( + public static final RaptorCostCalculator C1_CALCULATOR = new TestCostCalculator( BOARD_C1_SEC, TRANSFER_C1_SEC, WAIT_RELUCTANCE, - TRANSIT_RELUCTANCE, STOP_C1S ); diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Egress.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Egress.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Egress.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Egress.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/TestArrivals.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/TestArrivals.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/TestArrivals.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/TestArrivals.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transfer.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transfer.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transfer.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transfer.java diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transit.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transit.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transit.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/stoparrival/Transit.java diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/BoardAlightRestrictions.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/BoardAlightRestrictions.java new file mode 100644 index 00000000000..b18082f71ca --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/BoardAlightRestrictions.java @@ -0,0 +1,98 @@ +package org.opentripplanner.raptor._data.transit; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +public class BoardAlightRestrictions { + + private static final EnumSet BOARDING_ONLY = EnumSet.of( + BoardAlightRestriction.BOARDING + ); + private static final EnumSet ALIGHTING_ONLY = EnumSet.of( + BoardAlightRestriction.ALIGHTING + ); + private static final EnumSet ALL_ALLOWED = EnumSet.allOf( + BoardAlightRestriction.class + ); + private static final EnumSet NONE_ALLOWED = EnumSet.noneOf( + BoardAlightRestriction.class + ); + + private final List> restrictions; + + private BoardAlightRestrictions(List> restrictions) { + if (restrictions.isEmpty()) { + throw new IllegalArgumentException("At least one stop is required."); + } + this.restrictions = restrictions; + } + + public static BoardAlightRestrictions noRestriction(int size) { + var list = new ArrayList>(size); + for (int i = 0; i < size; i++) { + list.add(ALL_ALLOWED); + } + return new BoardAlightRestrictions(List.copyOf(list)); + } + + /** + * Set alight and board restriction using a "coded" string, use space as a separator + * between stops. + *
    +   * Codes:
    +   *   b : Board
    +   *   a : Alight
    +   *   * : Board & Alight
    +   *   - : Boarding & Alighting is not allowed
    +   *
    +   * Example:   B BA * A
    +   * 
    + */ + public static BoardAlightRestrictions restrictions(String restrictions) { + var codes = restrictions.toLowerCase().trim().split("\\s"); + var list = new ArrayList>(); + for (String code : codes) { + if ("a".equals(code)) { + list.add(ALIGHTING_ONLY); + } else if ("b".equals(code)) { + list.add(BOARDING_ONLY); + } else if (code.matches("ab|ba|\\*")) { + list.add(ALL_ALLOWED); + } else if ("-".equals(code)) { + list.add(NONE_ALLOWED); + } + } + return new BoardAlightRestrictions(list); + } + + public boolean isBoardingPossibleAt(int stopPositionInPattern) { + return restrictions.get(stopPositionInPattern).contains(BoardAlightRestriction.BOARDING); + } + + public boolean isAlightingPossibleAt(int stopPositionInPattern) { + return restrictions.get(stopPositionInPattern).contains(BoardAlightRestriction.ALIGHTING); + } + + @Override + public String toString() { + var buf = new StringBuilder(); + for (int i = 0; i < restrictions.size(); ++i) { + if (isAlightingPossibleAt(i) && isBoardingPossibleAt(i)) { + buf.append(" *"); + } else if (isAlightingPossibleAt(i)) { + buf.append(" A"); + } else if (isBoardingPossibleAt(i)) { + buf.append(" B"); + } else { + buf.append(" Ø"); + } + } + return buf.substring(1); + } + + private enum BoardAlightRestriction { + BOARDING, + ALIGHTING, + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java index c58240126de..4714c92939c 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestAccessEgress.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.raptor._data.RaptorTestConstants.SECONDS_IN_A_DAY; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; import java.util.ArrayList; import java.util.Collection; diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java similarity index 95% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java index 5e8980dc50a..2b0e09bd397 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedBoardingSearch.java @@ -9,7 +9,6 @@ import java.util.function.BiPredicate; import java.util.stream.Collectors; import javax.annotation.Nullable; -import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch; import org.opentripplanner.raptor.spi.RaptorTimeTable; @@ -55,7 +54,7 @@ public RaptorBoardOrAlightEvent find( var trip = tx.getSourceTrip(); if (trip == sourceTrip) { int stopPos = trip.findDepartureStopPosition(prevTransitArrivalTime, sourceStopIndex); - boolean boardAlightPossible = timeAfterOrEqual.test(tx.getTime(), prevTransitArrivalTime); + boolean boardAlightPossible = timeAfterOrEqual.test(tx.time(), prevTransitArrivalTime); if (tx.getSourceStopPos() == stopPos && boardAlightPossible) { return tx.boardingEvent(tx.isFacilitated() ? prevTransitArrivalTime : earliestBoardTime); } @@ -95,7 +94,7 @@ void addConstraintTransfers( int targetTripIndex, int targetStopPos, int targetTime, - TransferConstraint constraint + TestTransferConstraint constraint ) { List list = transfersByFromStopPos.get(targetStopPos); if (list == null) { diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java new file mode 100644 index 00000000000..132d20465ac --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestConstrainedTransfer.java @@ -0,0 +1,139 @@ +package org.opentripplanner.raptor._data.transit; + +import static org.opentripplanner.raptor.api.model.RaptorConstants.NOT_SET; + +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.opentripplanner.raptor.api.model.RaptorConstrainedTransfer; +import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; +import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +class TestConstrainedTransfer + implements RaptorConstrainedTransfer, RaptorBoardOrAlightEvent { + + private final TestTransferConstraint transferConstraint; + private final TestTripSchedule sourceTrip; + private final int sourceStopPos; + private final TestTripSchedule targetTrip; + private final int targetStopPos; + private final int targetTripIndex; + private final int targetTime; + + private int earliestBoardTime = NOT_SET; + + TestConstrainedTransfer( + TestTransferConstraint transferConstraint, + TestTripSchedule sourceTrip, + int sourceStopPos, + TestTripSchedule targetTrip, + int targetStopPos, + int targetTripIndex, + int targetTime + ) { + this.transferConstraint = transferConstraint; + this.sourceTrip = sourceTrip; + this.sourceStopPos = sourceStopPos; + this.targetTrip = targetTrip; + this.targetTripIndex = targetTripIndex; + this.targetStopPos = targetStopPos; + this.targetTime = targetTime; + } + + @Override + public int tripIndex() { + return targetTripIndex; + } + + @Override + public TestTripSchedule trip() { + return targetTrip; + } + + @Override + public int stopPositionInPattern() { + return targetStopPos; + } + + @Override + public int time() { + return targetTime; + } + + @Override + public int earliestBoardTime() { + return earliestBoardTime; + } + + @Override + public RaptorTransferConstraint transferConstraint() { + return transferConstraint; + } + + @Override + public boolean empty() { + return false; + } + + @Override + public void boardWithFallback( + Consumer> boardCallback, + Consumer> alternativeBoardingFallback + ) { + if (empty()) { + alternativeBoardingFallback.accept(this); + } else if (!transferConstraint.isNotAllowed()) { + boardCallback.accept(this); + } + } + + public boolean isFacilitated() { + return transferConstraint.isStaySeated() || transferConstraint.isGuaranteed(); + } + + @Nullable + @Override + public RaptorTransferConstraint getTransferConstraint() { + return transferConstraint; + } + + TestTripSchedule getSourceTrip() { + return sourceTrip; + } + + int getSourceStopPos() { + return sourceStopPos; + } + + RaptorBoardOrAlightEvent boardingEvent(int earliestBoardingTime) { + this.earliestBoardTime = earliestBoardingTime; + return this; + } + + public boolean match( + TestTripSchedule sourceTrip, + int sourceStopPos, + TestTripSchedule targetTrip, + int targetStopPos + ) { + return ( + this.sourceTrip.equals(sourceTrip) && + this.sourceStopPos == sourceStopPos && + this.targetTrip.equals(targetTrip) && + this.targetStopPos == targetStopPos + ); + } + + @Override + public String toString() { + return ToStringBuilder + .of(TestConstrainedTransfer.class) + .addObj("sourceTrip", sourceTrip) + .addNum("sourceStopPos", sourceStopPos) + .addObj("targetTrip", targetTrip) + .addNum("targetTripIndex", targetTripIndex) + .addNum("targetStopPos", targetStopPos) + .addServiceTime("targetTime", targetTime) + .toString(); + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestCostCalculator.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestCostCalculator.java new file mode 100644 index 00000000000..ab2d4ae3476 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestCostCalculator.java @@ -0,0 +1,199 @@ +package org.opentripplanner.raptor._data.transit; + +import javax.annotation.Nullable; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; +import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; +import org.opentripplanner.raptor.spi.RaptorCostCalculator; + +/** + * The responsibility for the cost calculator is to calculate the default multi-criteria cost. + *

    + * This class is immutable and thread safe. + */ +public final class TestCostCalculator implements RaptorCostCalculator { + + private static final int TRANSIT_RELUCTANCE = RaptorCostConverter.toRaptorCost(1); + + private final int boardCost; + private final int transferCost; + private final int waitFactor; + + /** + * Costs for boarding and alighting at a given stop during transfer. + * See TransitLayer.getStopBoardAlightTransferCosts() + */ + @Nullable + private final int[] stopBoardAlightTransferCosts; + + /** + * Cost unit: SECONDS - The unit for all input parameters are in the OTP TRANSIT model cost unit + * (in Raptor the unit for cost is centi-seconds). + * + * @param stopBoardAlightTransferCosts Unit centi-seconds. This parameter is used "as-is" and not + * transformed into the Raptor cast unit to avoid the transformation for each + * request. Use {@code null} to ignore stop cost. + */ + public TestCostCalculator( + int boardCost, + int transferCost, + double waitReluctanceFactor, + @Nullable int[] stopBoardAlightTransferCosts + ) { + this.boardCost = RaptorCostConverter.toRaptorCost(boardCost); + this.transferCost = RaptorCostConverter.toRaptorCost(transferCost); + this.waitFactor = RaptorCostConverter.toRaptorCost(waitReluctanceFactor); + this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts; + } + + @Override + public int boardingCost( + boolean firstBoarding, + int prevArrivalTime, + int boardStop, + int boardTime, + TestTripSchedule trip, + RaptorTransferConstraint transferConstraints + ) { + if (transferConstraints.isRegularTransfer()) { + return boardingCostRegularTransfer(firstBoarding, prevArrivalTime, boardStop, boardTime); + } else { + return boardingCostConstrainedTransfer( + prevArrivalTime, + boardStop, + boardTime, + firstBoarding, + transferConstraints + ); + } + } + + @Override + public int onTripRelativeRidingCost(int boardTime, TestTripSchedule tripScheduledBoarded) { + // The relative-transit-time is time spent on transit. We do not know the alight-stop, so + // it is impossible to calculate the "correct" time. But the only thing that maters is that + // the relative difference between to boardings are correct, assuming riding the same trip. + // So, we can use the negative board time as relative-transit-time. + return -boardTime * TRANSIT_RELUCTANCE; + } + + @Override + public int transitArrivalCost( + int boardCost, + int alightSlack, + int transitTime, + TestTripSchedule trip, + int toStop + ) { + int cost = boardCost + TRANSIT_RELUCTANCE * transitTime + waitFactor * alightSlack; + + // Add transfer cost on all alighting events. + // If it turns out to be the last one this cost will be removed during costEgress phase. + if (stopBoardAlightTransferCosts != null) { + cost += stopBoardAlightTransferCosts[toStop]; + } + + return cost; + } + + @Override + public int waitCost(int waitTimeInSeconds) { + return waitFactor * waitTimeInSeconds; + } + + @Override + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + if (minNumTransfers > -1) { + return ( + boardCost + + ((boardCost + transferCost) * minNumTransfers) + + (TRANSIT_RELUCTANCE * minTravelTime) + ); + } else { + // Remove cost that was added during alighting similar as we do in the costEgress() method + return stopBoardAlightTransferCosts == null + ? (TRANSIT_RELUCTANCE * minTravelTime) + : (TRANSIT_RELUCTANCE * minTravelTime) - stopBoardAlightTransferCosts[fromStop]; + } + } + + @Override + public int costEgress(RaptorAccessEgress egress) { + if (egress.hasRides()) { + return egress.c1() + transferCost; + } else if (stopBoardAlightTransferCosts != null) { + // Remove cost that was added during alighting. + // We do not want to add this cost on last alighting since it should only be applied on transfers + // It has to be done here because during alighting we do not know yet if it will be + // a transfer or not. + return egress.c1() - stopBoardAlightTransferCosts[egress.stop()]; + } else { + return egress.c1(); + } + } + + /** This is public for test purposes only */ + public int boardingCostRegularTransfer( + boolean firstBoarding, + int prevArrivalTime, + int boardStop, + int boardTime + ) { + // Calculate the wait-time before the boarding which should be accounted for in the cost + // calculation. Any slack at the end of the last leg is not part of this, because it is + // already accounted for. If the previous leg is an access leg, then it is already + // time-shifted, which is important for this calculation to be correct. + final int boardWaitTime = boardTime - prevArrivalTime; + + int cost = waitFactor * boardWaitTime; + + cost += firstBoarding ? boardCost : (boardCost + transferCost); + + // If it's first boarding event then it is not a transfer + if (stopBoardAlightTransferCosts != null && !firstBoarding) { + cost += stopBoardAlightTransferCosts[boardStop]; + } + return cost; + } + + /* private methods */ + + private int boardingCostConstrainedTransfer( + int prevArrivalTime, + int boardStop, + int boardTime, + boolean firstBoarding, + RaptorTransferConstraint txConstraints + ) { + // This cast could be avoided, if we added another generic type to the Raptor component, + // but it would be rather messy, just to avoid a single cast. + var tx = (TestTransferConstraint) txConstraints; + + if (tx.isStaySeated()) { + final int boardWaitTime = boardTime - prevArrivalTime; + // For a stay-seated transfer the wait-time is spent on-board and we should use the + // transitReluctance, not the waitReluctance, to find the cost of the time since + // the stop arrival. So we take the time and multiply it with the transit reluctance. + // + // Note! if the boarding happens BEFORE the previous stop arrival, we will get a + // negative time - this is ok, so we allow it in this calculation. + // + // The previous stop arrival might have a small alight-slack, this should be replaced + // with "on-board" time, but the slack should be short and the differance between + // transit reluctance and wait reluctance is also small, so we ignore this. + // + return TRANSIT_RELUCTANCE * boardWaitTime; + } else if (tx.isGuaranteed()) { + // For a guaranteed transfer we skip board- and transfer-cost + final int boardWaitTime = boardTime - prevArrivalTime; + + // StopBoardAlightTransferCost is NOT added to the cost here. This is because a trip-to-trip + // constrained transfer take precedence over stop-to-stop transfer priority (NeTEx station + // transfer priority). + return waitFactor * boardWaitTime; + } + + // fallback to regular transfer + return boardingCostRegularTransfer(firstBoarding, prevArrivalTime, boardStop, boardTime); + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java new file mode 100644 index 00000000000..2cd87a065ea --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestRoute.java @@ -0,0 +1,144 @@ +package org.opentripplanner.raptor._data.transit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.opentripplanner.raptor.api.model.SearchDirection; +import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch; +import org.opentripplanner.raptor.spi.RaptorRoute; +import org.opentripplanner.raptor.spi.RaptorTimeTable; +import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +public class TestRoute implements RaptorRoute, RaptorTimeTable { + + private final TestTripPattern pattern; + private final List schedules = new ArrayList<>(); + private final TestConstrainedBoardingSearch transferConstraintsForwardSearch = new TestConstrainedBoardingSearch( + true + ); + private final TestConstrainedBoardingSearch transferConstraintsReverseSearch = new TestConstrainedBoardingSearch( + false + ); + + private TestRoute(TestTripPattern pattern) { + this.pattern = pattern; + } + + public static TestRoute route(TestTripPattern pattern) { + return new TestRoute(pattern); + } + + public static TestRoute route(String name, int... stopIndexes) { + return route(TestTripPattern.pattern(name, stopIndexes)); + } + + /* RaptorRoute */ + + @Override + public RaptorTimeTable timetable() { + return this; + } + + @Override + public TestTripPattern pattern() { + return pattern; + } + + public RaptorConstrainedBoardingSearch transferConstraintsForwardSearch() { + return transferConstraintsForwardSearch; + } + + public RaptorConstrainedBoardingSearch transferConstraintsReverseSearch() { + return transferConstraintsReverseSearch; + } + + /* RaptorTimeTable */ + + @Override + public TestTripSchedule getTripSchedule(int index) { + return schedules.get(index); + } + + @Override + public int numberOfTripSchedules() { + return schedules.size(); + } + + @Override + public RaptorTripScheduleSearch tripSearch(SearchDirection direction) { + return new TestTripScheduleSearch(direction, schedules); + } + + public List listTransferConstraintsForwardSearch() { + return transferConstraintsForwardSearch.constrainedBoardings(); + } + + public TestRoute withTimetable(TestTripSchedule... trips) { + Collections.addAll(schedules, trips); + return this; + } + + public TestRoute withTimetable(TestTripSchedule.Builder... scheduleBuilders) { + for (TestTripSchedule.Builder builder : scheduleBuilders) { + var tripSchedule = builder.pattern(pattern).build(); + schedules.add(tripSchedule); + } + return this; + } + + @Override + public String toString() { + return ToStringBuilder + .of(TestRoute.class) + .addObj("pattern", pattern) + .addObj("schedules", schedules) + .toString(); + } + + void clearTransferConstraints() { + transferConstraintsForwardSearch.clear(); + transferConstraintsReverseSearch.clear(); + } + + /** + * Add a transfer constraint to the route by iterating over all trips and matching the provided + * {@code toTrip}(added to forward search) {@code fromTrip}(added to reverse search) with the rips + * in the route timetable. + */ + void addTransferConstraint( + TestTripSchedule fromTrip, + int fromStopPos, + TestTripSchedule toTrip, + int toStopPos, + TestTransferConstraint constraint + ) { + for (int i = 0; i < timetable().numberOfTripSchedules(); i++) { + var trip = timetable().getTripSchedule(i); + if (toTrip == trip) { + this.transferConstraintsForwardSearch.addConstraintTransfers( + fromTrip, + fromStopPos, + trip, + i, + toStopPos, + trip.arrival(toStopPos), + constraint + ); + } + // Reverse search transfer, the {@code source/target} is the trips in order of the + // reverse search, which is opposite from {@code from/to} in the result path. + if (fromTrip == trip) { + this.transferConstraintsReverseSearch.addConstraintTransfers( + toTrip, + toStopPos, + trip, + i, + fromStopPos, + trip.departure(fromStopPos), + constraint + ); + } + } + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java similarity index 86% rename from application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java rename to raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java index 5645f6342d6..b90fd5f7c46 100644 --- a/application/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransfer.java @@ -1,6 +1,6 @@ package org.opentripplanner.raptor._data.transit; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter.toRaptorCost; +import static org.opentripplanner.raptor.api.model.RaptorCostConverter.toRaptorCost; import org.opentripplanner.raptor.api.model.RaptorTransfer; @@ -18,6 +18,10 @@ public static TestTransfer transfer(int stop, int durationInSeconds, int cost) { return new TestTransfer(stop, durationInSeconds, cost); } + public TestTransfer reverse(int stop) { + return new TestTransfer(stop, durationInSeconds, cost); + } + public static int walkCost(int durationInSeconds) { return walkCost(durationInSeconds, DEFAULT_WALK_RELUCTANCE); } diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferConstraint.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferConstraint.java new file mode 100644 index 00000000000..c630c6a8bbb --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransferConstraint.java @@ -0,0 +1,84 @@ +package org.opentripplanner.raptor._data.transit; + +import java.io.Serializable; +import java.util.Objects; +import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; + +/** + * This class holds transfer constraint information. + *

    + * The class is immutable. + */ +public class TestTransferConstraint implements Serializable, RaptorTransferConstraint { + + private enum Type { + NOT_ALLOWED, + STAY_SEATED, + GUARANTEED; + + boolean is(Type type) { + return this == type; + } + } + + private final Type type; + + public TestTransferConstraint(Type type) { + this.type = type; + } + + public static TestTransferConstraint notAllowed() { + return new TestTransferConstraint(Type.NOT_ALLOWED); + } + + public static TestTransferConstraint staySeated() { + return new TestTransferConstraint(Type.STAY_SEATED); + } + + public static TestTransferConstraint guaranteed() { + return new TestTransferConstraint(Type.GUARANTEED); + } + + public static RaptorTransferConstraint regular() { + return RaptorTransferConstraint.REGULAR_TRANSFER; + } + + public boolean isGuaranteed() { + return type.is(Type.GUARANTEED); + } + + @Override + public boolean isNotAllowed() { + return type.is(Type.NOT_ALLOWED); + } + + @Override + public boolean isRegularTransfer() { + return false; + } + + @Override + public boolean isStaySeated() { + return type.is(Type.STAY_SEATED); + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof final TestTransferConstraint that)) { + return false; + } + return type == that.type; + } + + public String toString() { + return "{" + type + "}"; + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java new file mode 100644 index 00000000000..edd87c16b48 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -0,0 +1,347 @@ +package org.opentripplanner.raptor._data.transit; + +import static org.opentripplanner.raptor._data.transit.TestRoute.route; +import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; +import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor.api.model.RaptorConstrainedTransfer; +import org.opentripplanner.raptor.api.model.RaptorStopNameResolver; +import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.raptor.api.model.RaptorTripPattern; +import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; +import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; +import org.opentripplanner.raptor.spi.DefaultSlackProvider; +import org.opentripplanner.raptor.spi.IntIterator; +import org.opentripplanner.raptor.spi.RaptorConstrainedBoardingSearch; +import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.raptor.spi.RaptorPathConstrainedTransferSearch; +import org.opentripplanner.raptor.spi.RaptorRoute; +import org.opentripplanner.raptor.spi.RaptorSlackProvider; +import org.opentripplanner.raptor.spi.RaptorTimeTable; +import org.opentripplanner.raptor.spi.RaptorTransitDataProvider; +import org.opentripplanner.raptor.util.BitSetIterator; + +@SuppressWarnings("UnusedReturnValue") +public class TestTransitData + implements RaptorTransitDataProvider, RaptorTestConstants { + + public static final TestTransferConstraint TX_GUARANTEED = TestTransferConstraint.guaranteed(); + public static final TestTransferConstraint TX_NOT_ALLOWED = TestTransferConstraint.notAllowed(); + + // Slack defaults: 1 minute for transfer-slack, 0 minutes for board- and alight-slack. + public static final RaptorSlackProvider SLACK_PROVIDER = new DefaultSlackProvider(60, 0, 0); + + private final List> transfersFromStop = new ArrayList<>(); + private final List> transfersToStop = new ArrayList<>(); + private final List> routeIndexesByStopIndex = new ArrayList<>(); + private final List routes = new ArrayList<>(); + private final List constrainedTransfers = new ArrayList<>(); + private int boardCostSec = 600; + private int transferCostSec = 0; + private double waitReluctance = 1.0; + + private final int[] stopBoardAlightTransferCosts = new int[NUM_STOPS]; + + private RaptorSlackProvider slackProvider = SLACK_PROVIDER; + + @Override + public Iterator getTransfersFromStop(int fromStop) { + return transfersFromStop.get(fromStop).iterator(); + } + + @Override + public Iterator getTransfersToStop(int toStop) { + return transfersToStop.get(toStop).iterator(); + } + + @Override + public IntIterator routeIndexIterator(IntIterator stops) { + BitSet routes = new BitSet(); + while (stops.hasNext()) { + int stop = stops.next(); + for (int i : routeIndexesByStopIndex.get(stop)) { + routes.set(i); + } + } + return new BitSetIterator(routes); + } + + @Override + public RaptorRoute getRouteForIndex(int routeIndex) { + return this.routes.get(routeIndex); + } + + @Override + public int numberOfStops() { + return routeIndexesByStopIndex.size(); + } + + @Override + public RaptorCostCalculator multiCriteriaCostCalculator() { + return new TestCostCalculator( + boardCostSec, + transferCostSec, + waitReluctance, + stopBoardAlightTransferCosts() + ); + } + + @Override + public RaptorSlackProvider slackProvider() { + return slackProvider; + } + + public TestTransitData withSlackProvider(RaptorSlackProvider slackProvider) { + this.slackProvider = slackProvider; + return this; + } + + @Override + public RaptorPathConstrainedTransferSearch transferConstraintsSearch() { + return new RaptorPathConstrainedTransferSearch<>() { + @Nullable + @Override + public RaptorConstrainedTransfer findConstrainedTransfer( + TestTripSchedule fromTrip, + int fromStopPosition, + TestTripSchedule toTrip, + int toStopPosition + ) { + var list = routes + .stream() + .flatMap(r -> r.listTransferConstraintsForwardSearch().stream()) + .filter(tx -> tx.match(fromTrip, fromStopPosition, toTrip, toStopPosition)) + .toList(); + + if (list.isEmpty()) { + return null; + } + if (list.size() == 1) { + return list.get(0); + } + throw new IllegalStateException("More than on transfers found: " + list); + } + }; + } + + @Override + public RaptorStopNameResolver stopNameResolver() { + // Index is translated: 1->'A', 2->'B', 3->'C' ... + return this::stopIndexToName; + } + + @Override + public int getValidTransitDataStartTime() { + return this.routes.stream() + .mapToInt(route -> route.timetable().getTripSchedule(0).departure(0)) + .min() + .orElseThrow(); + } + + @Override + public int getValidTransitDataEndTime() { + return this.routes.stream() + .mapToInt(route -> { + RaptorTimeTable timetable = route.timetable(); + RaptorTripPattern pattern = route.pattern(); + return timetable + .getTripSchedule(timetable.numberOfTripSchedules() - 1) + .departure(pattern.numberOfStopsInPattern() - 1); + }) + .max() + .orElseThrow(); + } + + @Override + public RaptorConstrainedBoardingSearch transferConstraintsForwardSearch( + int routeIndex + ) { + return getRoute(routeIndex).transferConstraintsForwardSearch(); + } + + @Override + public RaptorConstrainedBoardingSearch transferConstraintsReverseSearch( + int routeIndex + ) { + return getRoute(routeIndex).transferConstraintsReverseSearch(); + } + + public TestRoute getRoute(int index) { + return routes.get(index); + } + + public void debugToStdErr(RaptorRequestBuilder request, boolean dryRun) { + var debug = request.debug(); + + if (debug.stops().isEmpty()) { + debug.addStops(stopsVisited()); + } + var logger = new SystemErrDebugLogger(true, dryRun); + + debug + .stopArrivalListener(logger::stopArrivalLister) + .patternRideDebugListener(logger::patternRideLister) + .pathFilteringListener(logger::pathFilteringListener) + .logger(logger); + } + + public TestTransitData withRoute(TestRoute route) { + this.routes.add(route); + int routeIndex = this.routes.indexOf(route); + var pattern = route.pattern(); + for (int i = 0; i < pattern.numberOfStopsInPattern(); ++i) { + int stopIndex = pattern.stopIndex(i); + expandNumOfStops(stopIndex); + routeIndexesByStopIndex.get(stopIndex).add(routeIndex); + } + return this; + } + + /** + * Same as: + *

    +   * withRoute(
    +   *   route(pattern(routeName, stopIndexes))
    +   *     .withTimetable(schedule().times(times))
    +   * )
    +   * 
    + */ + public TestTransitData withTransit(String routeName, String times, int... stopIndexes) { + return withRoute(route(pattern(routeName, stopIndexes)).withTimetable(schedule().times(times))); + } + + public TestTransitData withRoutes(TestRoute... routes) { + for (TestRoute route : routes) { + withRoute(route); + } + return this; + } + + public TestTransitData withTransfer(int fromStop, TestTransfer transfer) { + expandNumOfStops(Math.max(fromStop, transfer.stop())); + transfersFromStop.get(fromStop).add(transfer); + transfersToStop.get(transfer.stop()).add(transfer.reverse(fromStop)); + return this; + } + + public TestTransitData withTransferCost(int transferCostSec) { + this.transferCostSec = transferCostSec; + return this; + } + + public TestTransitData withGuaranteedTransfer( + TestTripSchedule fromTrip, + int fromStop, + TestTripSchedule toTrip, + int toStop + ) { + return withConstrainedTransfer(fromTrip, fromStop, toTrip, toStop, TX_GUARANTEED); + } + + public void clearConstrainedTransfers() { + constrainedTransfers.clear(); + for (TestRoute route : routes) { + route.clearTransferConstraints(); + } + } + + /** + * Create constraint for a given transfer. If trip passes through the stop more than once + * constraint will be placed on stop position for the first visit. + * @param fromTrip initial trip + * @param fromStop initial stop index + * @param toTrip destination trip + * @param toStop destination trip index + * @param constraint constraint to set + */ + public TestTransitData withConstrainedTransfer( + TestTripSchedule fromTrip, + int fromStop, + TestTripSchedule toTrip, + int toStop, + TestTransferConstraint constraint + ) { + int fromStopPos = fromTrip.pattern().findStopPositionAfter(0, fromStop); + int toStopPos = toTrip.pattern().findStopPositionAfter(0, toStop); + + for (TestRoute route : routes) { + route.addTransferConstraint(fromTrip, fromStopPos, toTrip, toStopPos, constraint); + } + constrainedTransfers.add( + new TestConstrainedTransfer( + constraint, + fromTrip, + fromStopPos, + toTrip, + toStopPos, + toTrip.tripSortIndex(), + toTrip.departure(toStopPos) + ) + ); + return this; + } + + public TestTransitData withStopBoardAlightTransferCost(int stop, int boardAlightTransferCost) { + stopBoardAlightTransferCosts[stop] = boardAlightTransferCost; + return this; + } + + public TestConstrainedTransfer findConstrainedTransfer( + TestTripSchedule fromTrip, + int fromStop, + int fromStopPosition, + TestTripSchedule toTrip, + int toStop, + int toStopPosition + ) { + for (var tx : constrainedTransfers) { + if (tx.match(fromTrip, fromStopPosition, toTrip, toStopPosition)) { + return tx; + } + } + return null; + } + + public TestTransitData withBoardCost(int boardCostSec) { + this.boardCostSec = boardCostSec; + return this; + } + + public TestTransitData withWaitReluctance(double waitReluctance) { + this.waitReluctance = waitReluctance; + return this; + } + + /* private methods */ + + private int[] stopBoardAlightTransferCosts() { + // Not implemented, no test for this yet. + return stopBoardAlightTransferCosts; + } + + private void expandNumOfStops(int stopIndex) { + for (int i = numberOfStops(); i <= stopIndex; ++i) { + transfersFromStop.add(new ArrayList<>()); + transfersToStop.add(new ArrayList<>()); + routeIndexesByStopIndex.add(new HashSet<>()); + } + } + + private List stopsVisited() { + final List stops = new ArrayList<>(); + for (int i = 0; i < routeIndexesByStopIndex.size(); i++) { + if (!routeIndexesByStopIndex.get(i).isEmpty()) { + stops.add(i); + } + } + return stops; + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java new file mode 100644 index 00000000000..e9b6613500b --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripPattern.java @@ -0,0 +1,160 @@ +package org.opentripplanner.raptor._data.transit; + +import java.util.Objects; +import org.opentripplanner.raptor.api.model.RaptorTripPattern; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +public class TestTripPattern implements RaptorTripPattern { + + private final String name; + private final int[] stopIndexes; + private final BoardAlightRestrictions restrictions; + private final int slackIndex; + private final int patternIndex; + private final int priorityGroupId; + + private TestTripPattern( + String name, + int[] stopIndexes, + BoardAlightRestrictions restrictions, + int slackIndex, + int patternIndex, + int priorityGroupId + ) { + this.name = Objects.requireNonNull(name); + this.stopIndexes = Objects.requireNonNull(stopIndexes); + this.restrictions = Objects.requireNonNull(restrictions); + this.slackIndex = slackIndex; + this.patternIndex = patternIndex; + this.priorityGroupId = priorityGroupId; + } + + public static TestTripPattern.Builder of(String name, int... stopIndexes) { + return new TestTripPattern.Builder(name, stopIndexes); + } + + public static TestTripPattern pattern(String name, int... stopIndexes) { + return of(name, stopIndexes).build(); + } + + /** Create a pattern with name 'R1' and given stop indexes */ + public static TestTripPattern pattern(int... stopIndexes) { + return pattern("R1", stopIndexes); + } + + @Override + public int stopIndex(int stopPositionInPattern) { + return stopIndexes[stopPositionInPattern]; + } + + @Override + public boolean boardingPossibleAt(int stopPositionInPattern) { + return restrictions.isBoardingPossibleAt(stopPositionInPattern); + } + + @Override + public boolean alightingPossibleAt(int stopPositionInPattern) { + return restrictions.isAlightingPossibleAt(stopPositionInPattern); + } + + @Override + public int slackIndex() { + return slackIndex; + } + + @Override + public int priorityGroupId() { + return priorityGroupId; + } + + @Override + public int patternIndex() { + return patternIndex; + } + + @Override + public int numberOfStopsInPattern() { + return stopIndexes.length; + } + + @Override + public String debugInfo() { + return "BUS " + name; + } + + @Override + public String toString() { + return ToStringBuilder + .of(TestTripPattern.class) + .addStr("name", name) + .addInts("stops", stopIndexes) + .addObj("restrictions", restrictions) + .toString(); + } + + public static class Builder { + + private final String name; + private int[] stopIndexes; + private String restrictions; + private int slackIndex = 0; + private int patternIndex = 0; + private int priorityGroupId = 0; + + public Builder(String name, int... stopIndexes) { + this.name = name; + this.stopIndexes = stopIndexes; + } + + public Builder pattern(int... stopIndexes) { + this.stopIndexes = stopIndexes; + return this; + } + + /** + * Set alight and board restriction using a "coded" string, use space as a separator + * between stops. + *
    +     * Codes:
    +     *   b : Board
    +     *   a : Alight
    +     *   * : Board & Alight
    +     *   - : Boarding & Alighting is not allowed
    +     *
    +     * Example:   B BA * A
    +     * 
    + */ + public Builder restrictions(String restrictions) { + this.restrictions = restrictions; + return this; + } + + public Builder slackIndex(int index) { + this.slackIndex = index; + return this; + } + + public Builder patternIndex(int index) { + this.patternIndex = index; + return this; + } + + public Builder priorityGroup(int priorityGroupId) { + this.priorityGroupId = priorityGroupId; + return this; + } + + public TestTripPattern build() { + return new TestTripPattern( + name, + stopIndexes, + restrictions == null + ? BoardAlightRestrictions.noRestriction(stopIndexes.length) + : BoardAlightRestrictions.restrictions(restrictions), + slackIndex, + patternIndex, + priorityGroupId + ); + } + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java new file mode 100644 index 00000000000..cbce4a642e0 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripSchedule.java @@ -0,0 +1,216 @@ +package org.opentripplanner.raptor._data.transit; + +import java.util.Arrays; +import java.util.stream.IntStream; +import org.opentripplanner.raptor.api.model.RaptorTripPattern; +import org.opentripplanner.raptor.api.model.RaptorTripSchedule; +import org.opentripplanner.utils.lang.IntUtils; +import org.opentripplanner.utils.time.TimeUtils; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +/** + * An implementation of the {@link RaptorTripSchedule} for unit-testing. + */ +public class TestTripSchedule implements RaptorTripSchedule { + + private static final int DEFAULT_DEPARTURE_DELAY = 10; + private final int[] arrivalTimes; + private final int[] departureTimes; + private final RaptorTripPattern pattern; + + protected TestTripSchedule(TestTripPattern pattern, int[] arrivalTimes, int[] departureTimes) { + this.pattern = pattern; + this.arrivalTimes = arrivalTimes; + this.departureTimes = departureTimes; + } + + public static TestTripSchedule.Builder schedule() { + return new TestTripSchedule.Builder(); + } + + public static TestTripSchedule.Builder schedule(TestTripPattern pattern) { + return schedule().pattern(pattern); + } + + public static TestTripSchedule.Builder schedule(String times) { + return new TestTripSchedule.Builder().times(times); + } + + @Override + public int tripSortIndex() { + // We sort trips based on the departure from the first stop + return arrival(0); + } + + @Override + public int arrival(int stopPosInPattern) { + return arrivalTimes[stopPosInPattern]; + } + + @Override + public int departure(int stopPosInPattern) { + return departureTimes[stopPosInPattern]; + } + + @Override + public RaptorTripPattern pattern() { + return pattern; + } + + public int size() { + return arrivalTimes.length; + } + + @Override + public String toString() { + if (Arrays.equals(arrivalTimes, departureTimes)) { + return ToStringBuilder + .of(TestTripSchedule.class) + .addServiceTimeSchedule("times", arrivalTimes) + .toString(); + } + return ToStringBuilder + .of(TestTripSchedule.class) + .addServiceTimeSchedule("arrivals", arrivalTimes) + .addServiceTimeSchedule("departures", departureTimes) + .toString(); + } + + @SuppressWarnings("UnusedReturnValue") + public static class Builder { + + private TestTripPattern pattern; + private int[] arrivalTimes; + private int[] departureTimes; + private int arrivalDepartureOffset = DEFAULT_DEPARTURE_DELAY; + + public TestTripSchedule.Builder pattern(TestTripPattern pattern) { + this.pattern = pattern; + return this; + } + + public TestTripSchedule.Builder copy() { + var b = new TestTripSchedule.Builder(); + b.pattern = pattern; + b.arrivalTimes = arrivalTimes; + b.departureTimes = departureTimes; + b.arrivalDepartureOffset = arrivalDepartureOffset; + return b; + } + + public TestTripSchedule.Builder pattern(String name, int... stops) { + return pattern(TestTripPattern.pattern(name, stops)); + } + + /** @param times departure and arrival times per stop. Example: "0:10, 0:20, 0:45 .." */ + public TestTripSchedule.Builder times(String times) { + return times(TimeUtils.times(times)); + } + + /** @param times departure and arrival times per stop in seconds past midnight. */ + public TestTripSchedule.Builder times(int... times) { + arrivals(times); + departures(times); + return this; + } + + /** @param arrivalTimes arrival times per stop. Example: "0:10, 0:20, 0:45 .. */ + public TestTripSchedule.Builder arrivals(String arrivalTimes) { + return this.arrivals(TimeUtils.times(arrivalTimes)); + } + + /** @param arrivalTimes arrival times per stop in seconds past midnight. */ + public TestTripSchedule.Builder arrivals(int... arrivalTimes) { + this.arrivalTimes = arrivalTimes; + return this; + } + + /** @param departureTimes departure times per stop. Example: "0:10, 0:20, 0:45 .. */ + public TestTripSchedule.Builder departures(String departureTimes) { + return this.departures(TimeUtils.times(departureTimes)); + } + + /** @param departureTimes departure times per stop in seconds past midnight. */ + public TestTripSchedule.Builder departures(int... departureTimes) { + this.departureTimes = departureTimes; + return this; + } + + /** + * The time between arrival and departure for each stop in the pattern. If not both arrival and + * departure times are set, this parameter is used to calculate the unset values. + *

    + * Unit: seconds. The default is 10 seconds. + */ + public TestTripSchedule.Builder arrDepOffset(int arrivalDepartureOffset) { + this.arrivalDepartureOffset = arrivalDepartureOffset; + return this; + } + + /** + * Shift all arrival/departure times by the given {@code offset}. Be careful, this + * method change the builder instance, use {@link #copy()} if you need the original. + *

    + * Offset unit is seconds. + */ + public TestTripSchedule.Builder shiftTimes(int offset) { + if (arrivalTimes == departureTimes) { + arrivalTimes = departureTimes = IntUtils.shiftArray(offset, arrivalTimes); + } else { + if (arrivalTimes != null) { + arrivalTimes = IntUtils.shiftArray(offset, arrivalTimes); + } + if (departureTimes != null) { + departureTimes = IntUtils.shiftArray(offset, departureTimes); + } + } + return this; + } + + public TestTripSchedule.Builder[] repeat(int nTimes, int everySeconds) { + return IntStream + .range(0, nTimes) + .mapToObj(i -> copy().shiftTimes(i * everySeconds)) + .toArray(Builder[]::new); + } + + public TestTripSchedule build() { + if (arrivalTimes == null) { + arrivalTimes = copyWithOffset(departureTimes, -arrivalDepartureOffset); + } else if (departureTimes == null) { + departureTimes = copyWithOffset(arrivalTimes, arrivalDepartureOffset); + } + if (arrivalTimes.length != departureTimes.length) { + throw new IllegalStateException( + "Number of arrival and departure times do not match." + + " Arrivals: " + + arrivalTimes.length + + ", departures: " + + arrivalTimes.length + ); + } + if (pattern == null) { + pattern = TestTripPattern.pattern("DummyPattern", new int[arrivalTimes.length]); + } + if (arrivalTimes.length != pattern.numberOfStopsInPattern()) { + throw new IllegalStateException( + "Number of arrival and departure times do not match stops in pattern." + + " Arrivals/departures: " + + arrivalTimes.length + + ", stops: " + + pattern.numberOfStopsInPattern() + ); + } + + return new TestTripSchedule(pattern, arrivalTimes, departureTimes); + } + + private static int[] copyWithOffset(int[] source, int offset) { + int[] target = new int[source.length]; + for (int i = 0; i < source.length; i++) { + target[i] = source[i] + offset; + } + return target; + } + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearch.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearch.java new file mode 100644 index 00000000000..7e9a73e162e --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearch.java @@ -0,0 +1,122 @@ +package org.opentripplanner.raptor._data.transit; + +import static org.opentripplanner.raptor.api.model.RaptorConstants.NOT_FOUND; +import static org.opentripplanner.raptor.api.model.RaptorConstants.TIME_NOT_SET; + +import java.util.List; +import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; +import org.opentripplanner.raptor.api.model.SearchDirection; +import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; +import org.opentripplanner.raptor.spi.RaptorTripScheduleSearch; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +class TestTripScheduleSearch + implements + RaptorTripScheduleSearch, RaptorBoardOrAlightEvent { + + private final List trips; + private final SearchDirection direction; + + private int tripIndex; + private int stopPositionInPattern; + /** + * earliest-board-time in forward search, and latest-arrival-time for reverse search + */ + private int timeLimit; + private int time; + + public TestTripScheduleSearch(SearchDirection direction, List trips) { + this.trips = trips; + this.direction = direction; + this.tripIndex = NOT_FOUND; + this.time = TIME_NOT_SET; + } + + @Override + public RaptorBoardOrAlightEvent search( + int earliestBoardTime, + int stopPositionInPattern, + int tripIndexLimit + ) { + this.tripIndex = NOT_FOUND; + this.stopPositionInPattern = stopPositionInPattern; + this.timeLimit = earliestBoardTime; + + return direction.isForward() ? searchForward(tripIndexLimit) : searchInReverse(tripIndexLimit); + } + + private RaptorBoardOrAlightEvent searchForward(int tripIndexLimit) { + final int end = tripIndexLimit == UNBOUNDED_TRIP_INDEX ? trips.size() - 1 : tripIndexLimit - 1; + + for (int i = 0; i <= end; ++i) { + int departureTime = trips.get(i).departure(stopPositionInPattern); + if (timeLimit <= departureTime) { + this.time = departureTime; + this.tripIndex = i; + return this; + } + } + return RaptorBoardOrAlightEvent.empty(timeLimit); + } + + private RaptorBoardOrAlightEvent searchInReverse(int tripIndexLimit) { + final int end = tripIndexLimit == UNBOUNDED_TRIP_INDEX ? 0 : tripIndexLimit + 1; + + for (int i = trips.size() - 1; i >= end; --i) { + int arrivalTime = trips.get(i).arrival(stopPositionInPattern); + if (timeLimit >= arrivalTime) { + this.time = arrivalTime; + this.tripIndex = i; + return this; + } + } + return RaptorBoardOrAlightEvent.empty(timeLimit); + } + + @Override + public int tripIndex() { + return tripIndex; + } + + @Override + public TestTripSchedule trip() { + return trips.get(tripIndex); + } + + @Override + public int stopPositionInPattern() { + return stopPositionInPattern; + } + + @Override + public int time() { + return time; + } + + @Override + public int earliestBoardTime() { + return timeLimit; + } + + @Override + public RaptorTransferConstraint transferConstraint() { + return RaptorTransferConstraint.REGULAR_TRANSFER; + } + + @Override + public boolean empty() { + return tripIndex == NOT_FOUND; + } + + @Override + public String toString() { + return ToStringBuilder + .of(TestTripScheduleSearch.class) + .addBoolIfTrue("REVERSE", direction.isInReverse()) + .addNum("tripIndex", tripIndex, NOT_FOUND) + .addNum("stopPos", stopPositionInPattern) + .addServiceTime("timeLimit", timeLimit) + .addServiceTime("time", time, TIME_NOT_SET) + .toString(); + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearchTest.java b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearchTest.java new file mode 100644 index 00000000000..db4ff4610ba --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_data/transit/TestTripScheduleSearchTest.java @@ -0,0 +1,175 @@ +package org.opentripplanner.raptor._data.transit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.raptor.api.model.RaptorConstants.NOT_FOUND; +import static org.opentripplanner.raptor.api.model.SearchDirection.FORWARD; +import static org.opentripplanner.raptor.api.model.SearchDirection.REVERSE; + +import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor.api.model.SearchDirection; +import org.opentripplanner.utils.time.TimeUtils; + +/** + * This tests another test class - we do it to make sure the + * {@link TestTripScheduleSearch} is following the Raptor SPI contract. + */ +class TestTripScheduleSearchTest implements RaptorTestConstants { + + private static final int NOT_DEFINED = -999_999; + private static final int T09_59_59 = TimeUtils.time("09:59:59"); + private static final int T10_00_00 = TimeUtils.time("10:00:00"); + private static final int T10_00_01 = TimeUtils.time("10:00:01"); + private static final int T10_09_59 = TimeUtils.time("10:09:59"); + private static final int T10_10_00 = TimeUtils.time("10:10:00"); + private static final int T10_10_01 = TimeUtils.time("10:10:01"); + private static final int T10_19_59 = TimeUtils.time("10:19:59"); + private static final int T10_20_00 = TimeUtils.time("10:20:00"); + private static final int T10_20_01 = TimeUtils.time("10:20:01"); + + private static final int TRIP_ONE = 0; + private static final int TRIP_TWO = 1; + + private static final TestTripPattern PATTERN = TestTripPattern.pattern("R1", STOP_A, STOP_B); + + private static final List ONE_TRIP = List.of( + TestTripSchedule.schedule(PATTERN).times("10:00 10:10").build() + ); + + private static final List TWO_TRIPS = List.of( + TestTripSchedule.schedule(PATTERN).times("10:00 10:10").build(), + TestTripSchedule.schedule(PATTERN).times("10:10 10:20").build() + ); + + static List tripSearchWithOneTripTestCases() { + return List.of( + Arguments.of(FORWARD, STOP_POS_0, T09_59_59, T10_00_00, Status.OK), + Arguments.of(FORWARD, STOP_POS_0, T10_00_00, T10_00_00, Status.OK), + Arguments.of(FORWARD, STOP_POS_0, T10_00_01, NOT_DEFINED, Status.EMPTY), + Arguments.of(FORWARD, STOP_POS_1, T10_09_59, T10_10_00, Status.OK), + Arguments.of(FORWARD, STOP_POS_1, T10_10_00, T10_10_00, Status.OK), + Arguments.of(FORWARD, STOP_POS_1, T10_10_01, NOT_DEFINED, Status.EMPTY), + Arguments.of(REVERSE, STOP_POS_0, T09_59_59, NOT_DEFINED, Status.EMPTY), + Arguments.of(REVERSE, STOP_POS_0, T10_00_00, T10_00_00, Status.OK), + Arguments.of(REVERSE, STOP_POS_0, T10_00_01, T10_00_00, Status.OK), + Arguments.of(REVERSE, STOP_POS_1, T10_09_59, NOT_DEFINED, Status.EMPTY), + Arguments.of(REVERSE, STOP_POS_1, T10_10_00, T10_10_00, Status.OK), + Arguments.of(REVERSE, STOP_POS_1, T10_10_01, T10_10_00, Status.OK) + ); + } + + @ParameterizedTest + @MethodSource("tripSearchWithOneTripTestCases") + void tripSearchWithOneTrip( + SearchDirection direction, + int stopPos, + int searchTime, + int expTime, + Status expStatus + ) { + var search = new TestTripScheduleSearch(direction, ONE_TRIP); + var result = search.search(searchTime, stopPos); + + if (expStatus == Status.EMPTY) { + assertTrue(result.empty()); + assertTime(searchTime, result.earliestBoardTime()); + assertEquals(NOT_FOUND, result.tripIndex()); + } else if (expStatus == Status.OK) { + assertFalse(result.empty()); + assertEquals(stopPos, result.stopPositionInPattern()); + assertTime(expTime, result.time()); + assertTime(searchTime, result.earliestBoardTime()); + assertEquals(0, result.tripIndex()); + } + + if (!result.empty()) { + // A second search fails if the first search succeeded + int tripIndex = result.tripIndex(); + var e = search.search(direction.isForward() ? T09_59_59 : T10_20_01, stopPos, tripIndex); + assertTrue(e.empty()); + } + } + + static List tripSearchWithTwoTripsTestCases() { + return List.of( + Arguments.of(FORWARD, STOP_POS_0, T09_59_59, T10_00_00, TRIP_ONE), + Arguments.of(FORWARD, STOP_POS_0, T10_00_00, T10_00_00, TRIP_ONE), + Arguments.of(FORWARD, STOP_POS_0, T10_00_01, T10_10_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_0, T10_09_59, T10_10_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_0, T10_10_00, T10_10_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_0, T10_10_01, NOT_DEFINED, NOT_FOUND), + Arguments.of(FORWARD, STOP_POS_1, T10_09_59, T10_10_00, TRIP_ONE), + Arguments.of(FORWARD, STOP_POS_1, T10_10_00, T10_10_00, TRIP_ONE), + Arguments.of(FORWARD, STOP_POS_1, T10_10_01, T10_20_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_1, T10_19_59, T10_20_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_1, T10_20_00, T10_20_00, TRIP_TWO), + Arguments.of(FORWARD, STOP_POS_1, T10_20_01, NOT_DEFINED, NOT_FOUND), + Arguments.of(REVERSE, STOP_POS_0, T09_59_59, NOT_DEFINED, NOT_FOUND), + Arguments.of(REVERSE, STOP_POS_0, T10_00_00, T10_00_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_0, T10_00_01, T10_00_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_0, T10_09_59, T10_00_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_0, T10_10_00, T10_10_00, TRIP_TWO), + Arguments.of(REVERSE, STOP_POS_0, T10_10_01, T10_10_00, TRIP_TWO), + Arguments.of(REVERSE, STOP_POS_1, T10_09_59, NOT_DEFINED, NOT_FOUND), + Arguments.of(REVERSE, STOP_POS_1, T10_10_00, T10_10_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_1, T10_10_01, T10_10_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_1, T10_19_59, T10_10_00, TRIP_ONE), + Arguments.of(REVERSE, STOP_POS_1, T10_20_00, T10_20_00, TRIP_TWO), + Arguments.of(REVERSE, STOP_POS_1, T10_20_01, T10_20_00, TRIP_TWO) + ); + } + + @ParameterizedTest + @MethodSource("tripSearchWithTwoTripsTestCases") + void tripSearchWithTwoTrips( + SearchDirection direction, + int stopPos, + int searchTime, + int expTime, + int expTripIndex + ) { + var search = new TestTripScheduleSearch(direction, TWO_TRIPS); + var result = search.search(searchTime, stopPos); + + if (expTripIndex == NOT_FOUND) { + assertTrue(result.empty()); + assertTime(searchTime, result.earliestBoardTime()); + assertEquals(NOT_FOUND, result.tripIndex()); + } else { + assertFalse(result.empty()); + assertEquals(stopPos, result.stopPositionInPattern()); + assertTime(expTime, result.time()); + assertTime(searchTime, result.earliestBoardTime()); + assertEquals(expTripIndex, result.tripIndex()); + } + + if (!result.empty()) { + int secondTrip = direction.isForward() ? TRIP_TWO : TRIP_ONE; + int firstTrip = direction.isForward() ? TRIP_ONE : TRIP_TWO; + int tripIndex = result.tripIndex(); + var e = search.search(direction.isForward() ? T09_59_59 : T10_20_01, stopPos, tripIndex); + + if (tripIndex == secondTrip) { + assertFalse(e.empty()); + assertEquals(firstTrip, result.tripIndex()); + } else { + assertTrue(e.empty()); + assertEquals(NOT_FOUND, result.tripIndex()); + } + } + } + + private static void assertTime(int expected, int actual) { + assertEquals(TimeUtils.timeToStrLong(expected), TimeUtils.timeToStrLong(actual)); + } + + private enum Status { + OK, + EMPTY, + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/ArchComponent.java b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/ArchComponent.java new file mode 100644 index 00000000000..eb95bfaf708 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/ArchComponent.java @@ -0,0 +1,35 @@ +package org.opentripplanner.raptor._support.arch; + +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; +import java.util.Collection; + +public interface ArchComponent { + /** + * ArchUnit cached set of classes in OTP. It takes a bit of time to build the set of + * classes, so it is nice to avoid this for every test. ArchUnit also support JUnit5 + * with a @ArchTest annotation which cache and inject the classes, but the test become + * slightly more complex by using it and chasing it here works fine. + */ + JavaClasses OTP_CLASSES = new ClassFileImporter() + .withImportOption(new ImportOption.DoNotIncludeTests()) + .importPackages("org.opentripplanner"); + + /** + * All Java packages in {@code java.*} and {@code javax.*} + */ + Module JAVA_PACKAGES = Module.of( + Package.of("java.."), + Package.of("javax.(*).."), + Package.of("jakarta.(*)..") + ); + + Module LOG_FRAMEWORK = Module.of(Package.of("org.slf4j")); + + Collection packages(); + + default Collection packageIdentifiers() { + return packages().stream().map(Package::packageIdentifier).toList(); + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Module.java b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Module.java new file mode 100644 index 00000000000..c6b16a8faf5 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Module.java @@ -0,0 +1,23 @@ +package org.opentripplanner.raptor._support.arch; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class Module implements ArchComponent { + + private final List packages; + + private Module(List packages) { + this.packages = packages; + } + + public static Module of(ArchComponent... components) { + return new Module(Arrays.stream(components).flatMap(c -> c.packages().stream()).toList()); + } + + @Override + public Collection packages() { + return packages; + } +} diff --git a/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Package.java b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Package.java new file mode 100644 index 00000000000..d87daf06448 --- /dev/null +++ b/raptor/src/test/java/org/opentripplanner/raptor/_support/arch/Package.java @@ -0,0 +1,91 @@ +package org.opentripplanner.raptor._support.arch; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; + +import com.tngtech.archunit.lang.ArchRule; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@SuppressWarnings("UnusedReturnValue") +public class Package implements ArchComponent { + + private final String packageIdentifier; + private final Set allowedPackages = new HashSet<>(); + + private Package(String packageIdentifier) { + this.packageIdentifier = packageIdentifier; + } + + public static Package of(String packageIdentifier) { + return new Package(packageIdentifier); + } + + public Package subPackage(String packageIdentifier) { + return new Package(this.packageIdentifier + "." + packageIdentifier); + } + + public String packageIdentifier() { + return packageIdentifier; + } + + public String packageIdentifierAllSubPackages() { + return packageIdentifier + ".(**)"; + } + + public Package dependsOn(ArchComponent... allowedDependencies) { + for (ArchComponent it : allowedDependencies) { + this.allowedPackages.addAll(it.packages()); + } + return this; + } + + @Override + public Set packages() { + return Set.of(this); + } + + public Package verify() { + ArchRule rule = classes() + .that() + .resideInAPackage(packageIdentifier) + .should() + .onlyDependOnClassesThat() + .resideInAnyPackage(allAllowedPackages()); + + rule.check(OTP_CLASSES); + return this; + } + + /** + * This includes the allowed packages plus all globally allowed packages like: + *

      + *
    • Java framework
    • + *
    • Log framework
    • + *
    • Arrays (in package "")
    • + *
    + */ + private String[] allAllowedPackages() { + List all = new ArrayList<>(); + + for (Package p : allowedPackages) { + all.add(p.packageIdentifier()); + } + // Allow all packages to depend on Java + for (Package p : JAVA_PACKAGES.packages()) { + all.add(p.packageIdentifier()); + } + // Allow all packages to depend on the log framework + for (Package p : LOG_FRAMEWORK.packages()) { + all.add(p.packageIdentifier()); + } + // Allow all packages to depend on itself + all.add(packageIdentifier); + + // Allow all packages to depend on array types in package "" (empty) + all.add(""); + + return all.toArray(String[]::new); + } +} diff --git a/application/src/test/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunctionTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunctionTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunctionTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/model/GeneralizedCostRelaxFunctionTest.java diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverterTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/model/RaptorCostConverterTest.java similarity index 95% rename from application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverterTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/model/RaptorCostConverterTest.java index 3827bdd31b6..036ab4d4346 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/RaptorCostConverterTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/api/model/RaptorCostConverterTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; +package org.opentripplanner.raptor.api.model; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/application/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java index 7ea3c9805f5..e0819717e10 100644 --- a/application/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/api/path/PathTest.java @@ -18,10 +18,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.Test; -import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase; import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTransferConstraint; import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.path.Path; @@ -176,7 +176,7 @@ public void testCountTransfersWithStaySeated() { .times(time("09:10"), time("09:20")) .build(); - var tx = TransferConstraint.of().staySeated().build(); + var tx = TestTransferConstraint.staySeated(); TransitPathLeg leg2 = new TransitPathLeg<>( trip2, trip2.departure(0), diff --git a/application/src/test/java/org/opentripplanner/raptor/api/path/RaptorPathTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/path/RaptorPathTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/path/RaptorPathTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/path/RaptorPathTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequestTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequestTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequestTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequestTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/request/PassThroughPointTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/request/RaptorRequestTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/request/RaptorRequestTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/request/RaptorRequestTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/request/RaptorRequestTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/request/SearchParamsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/request/SearchParamsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/request/SearchParamsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/request/SearchParamsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/request/ViaLocationTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/request/ViaLocationTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/request/ViaLocationTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/request/ViaLocationTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/api/view/BoardAndAlightTimeTest.java b/raptor/src/test/java/org/opentripplanner/raptor/api/view/BoardAndAlightTimeTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/api/view/BoardAndAlightTimeTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/api/view/BoardAndAlightTimeTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/A01_SingleRouteTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/A01_SingleRouteTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/A01_SingleRouteTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/A01_SingleRouteTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/A04_BoardingTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/A04_BoardingTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/A04_BoardingTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/A04_BoardingTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/B01_AccessTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B01_AccessTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/B01_AccessTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/B01_AccessTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/B02_EgressTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B02_EgressTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/B02_EgressTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/B02_EgressTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/B03_AccessEgressTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B03_AccessEgressTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/B03_AccessEgressTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/B03_AccessEgressTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/B04_AccessEgressBoardingTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B04_AccessEgressBoardingTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/B04_AccessEgressBoardingTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/B04_AccessEgressBoardingTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java index df38aabc483..7f2dd2f0072 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java @@ -39,7 +39,7 @@ void setup() { .withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14"))) .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); - data.mcCostParamsBuilder().transferCost(0).boardCost(0); + data.withTransferCost(0).withBoardCost(0); data.withStopBoardAlightTransferCost(STOP_D, 60000); requestBuilder diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/C01_TransferBoardAndAlightSlackTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/C01_TransferBoardAndAlightSlackTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/C01_TransferBoardAndAlightSlackTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/C01_TransferBoardAndAlightSlackTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/C02_OnStreetTransfersTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/C02_OnStreetTransfersTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/C02_OnStreetTransfersTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/C02_OnStreetTransfersTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/C03_OnBoardArrivalDominateTransfersTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/C03_OnBoardArrivalDominateTransfersTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/C03_OnBoardArrivalDominateTransfersTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/C03_OnBoardArrivalDominateTransfersTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java similarity index 94% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java index a63c208cae3..cde91ab61f8 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/D01_SingeRouteBoardAlightRestrictionsTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.multiCriteria; import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.standard; @@ -57,8 +56,10 @@ public class D01_SingeRouteBoardAlightRestrictionsTest implements RaptorTestCons */ @BeforeEach void setup() { - TestTripPattern pattern = pattern("R1", STOP_B, STOP_C, STOP_D); - pattern.restrictions("BW BA AW"); + TestTripPattern pattern = TestTripPattern + .of("R1", STOP_B, STOP_C, STOP_D) + .restrictions("B BA A") + .build(); data.withRoute(route(pattern).withTimetable(schedule("00:01, 00:03, 00:05"))); requestBuilder .searchParams() diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java similarity index 93% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java index d1d1f2191f5..16d0ea745fe 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E01_StaySeatedTransferTest.java @@ -10,11 +10,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.model.transfer.TransferConstraint; import org.opentripplanner.raptor.RaptorService; import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor._data.api.PathUtils; import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTransferConstraint; import org.opentripplanner.raptor._data.transit.TestTransitData; import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; @@ -57,14 +57,8 @@ public void setup() { var tripB = r2.timetable().getTripSchedule(0); data.withRoutes(r1, r2); - data.withConstrainedTransfer( - tripA, - STOP_B, - tripB, - STOP_B, - TransferConstraint.of().staySeated().build() - ); - data.mcCostParamsBuilder().transferCost(100); + data.withConstrainedTransfer(tripA, STOP_B, tripB, STOP_B, TestTransferConstraint.staySeated()); + data.withTransferCost(100); // NOTE! No search-window is set. requestBuilder diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java index c448560c7fb..cd2b1f3c25e 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E02_GuaranteedWalkTransferTest.java @@ -61,7 +61,7 @@ public void setup() { data.withRoutes(r1, r2); data.withGuaranteedTransfer(tripA, STOP_B, tripB, STOP_C); data.withTransfer(STOP_B, TestTransfer.transfer(STOP_C, 30)); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); // NOTE! No search-window is set. requestBuilder diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java index 0866042548d..1a324796784 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/E03_NotAllowedConstrainedTransferTest.java @@ -64,7 +64,7 @@ public void setup() { // transit model, a not-allowed transfer should apply to ALL trips if constraint is passed // to raptor. data.withConstrainedTransfer(tripR1a, STOP_B, tripR2a, STOP_B, TestTransitData.TX_NOT_ALLOWED); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); // NOTE! No search-window set requestBuilder diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java index e93f464826c..96a5a04d7fd 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F01_AccessWithRidesTest.java @@ -20,12 +20,12 @@ import org.opentripplanner.raptor._data.RaptorTestConstants; import org.opentripplanner.raptor._data.transit.TestTransitData; import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; import org.opentripplanner.raptor.spi.DefaultSlackProvider; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** * FEATURE UNDER TEST diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F02_EgressWithRidesTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F02_EgressWithRidesTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F02_EgressWithRidesTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F02_EgressWithRidesTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F03_AccessEgressWithRidesBoardAndAlightSlackTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F03_AccessEgressWithRidesBoardAndAlightSlackTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F03_AccessEgressWithRidesBoardAndAlightSlackTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F03_AccessEgressWithRidesBoardAndAlightSlackTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F04_AccessEgressWithRidesNoTransitTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F04_AccessEgressWithRidesNoTransitTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F04_AccessEgressWithRidesNoTransitTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F04_AccessEgressWithRidesNoTransitTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F05_OnBoardAccessEgressAndTransfersTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F11_AccessWithRidesMultipleOptimalPathsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F11_AccessWithRidesMultipleOptimalPathsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F11_AccessWithRidesMultipleOptimalPathsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F11_AccessWithRidesMultipleOptimalPathsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java index 1ec5399a668..40e4ffd567d 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/F12_EgressWithRidesMultipleOptimalPathsTest.java @@ -25,12 +25,12 @@ import org.opentripplanner.raptor._data.transit.TestTransfer; import org.opentripplanner.raptor._data.transit.TestTransitData; import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; import org.opentripplanner.raptor.spi.DefaultSlackProvider; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** * FEATURE UNDER TEST diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G01_AccessWithOpeningHoursTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G01_AccessWithOpeningHoursTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G01_AccessWithOpeningHoursTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G01_AccessWithOpeningHoursTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G02_EgressWithOpeningHoursTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G02_EgressWithOpeningHoursTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G02_EgressWithOpeningHoursTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G02_EgressWithOpeningHoursTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G03_AccessWithOpeningHoursMultipleOptionsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G03_AccessWithOpeningHoursMultipleOptionsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G03_AccessWithOpeningHoursMultipleOptionsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G03_AccessWithOpeningHoursMultipleOptionsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G04_EgressWithOpeningHoursMultipleOptionsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G04_EgressWithOpeningHoursMultipleOptionsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G04_EgressWithOpeningHoursMultipleOptionsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G04_EgressWithOpeningHoursMultipleOptionsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G05_ClosedAccessOpeningHoursTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G05_ClosedAccessOpeningHoursTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G05_ClosedAccessOpeningHoursTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G05_ClosedAccessOpeningHoursTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/G06_ClosedEgressOpeningHoursTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/G06_ClosedEgressOpeningHoursTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/G06_ClosedEgressOpeningHoursTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/G06_ClosedEgressOpeningHoursTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java similarity index 96% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java index 9cf2aaf7b82..64e4f728d09 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/H11_GuaranteedTransferWithFlexAccessTest.java @@ -20,11 +20,11 @@ import org.opentripplanner.raptor._data.transit.TestTransfer; import org.opentripplanner.raptor._data.transit.TestTransitData; import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** * FEATURE UNDER TEST @@ -54,7 +54,7 @@ public void setup() { data.withRoutes(r1, r2); data.withGuaranteedTransfer(tripA, STOP_C, tripB, STOP_C); data.withTransfer(STOP_A, TestTransfer.transfer(STOP_B, D10m)); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); requestBuilder .searchParams() diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/I01_HeuristicTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/I01_HeuristicTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/I01_HeuristicTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/I01_HeuristicTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java similarity index 98% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java index d37a1adf61f..6047280488a 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/J01_PassThroughTest.java @@ -1,7 +1,7 @@ package org.opentripplanner.raptor.moduletests; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.model.plan.PlanTestConstants.D2m; +import static org.opentripplanner.raptor._data.RaptorTestConstants.D2m; import static org.opentripplanner.raptor._data.RaptorTestConstants.D30s; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_A; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_B; @@ -163,7 +163,7 @@ void passThroughPointInTheMiddle() { var r2 = route("R2", STOP_A, STOP_C, STOP_D).withTimetable(schedule("0:02 0:10 0:50")); data.withRoutes(r1, r2); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); var requestBuilder = prepareRequest(); @@ -195,7 +195,7 @@ void multiplePassThroughPoints() { .withTimetable(schedule("0:15 0:20 0:30 0:50")); data.withRoutes(r1, r2); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); var requestBuilder = prepareRequest(); diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java similarity index 99% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java index 581cd117cb1..fd872a3d138 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/J02_ViaSearchTest.java @@ -1,8 +1,8 @@ package org.opentripplanner.raptor.moduletests; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.model.plan.PlanTestConstants.D2m; import static org.opentripplanner.raptor._data.RaptorTestConstants.D1m; +import static org.opentripplanner.raptor._data.RaptorTestConstants.D2m; import static org.opentripplanner.raptor._data.RaptorTestConstants.D30s; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_A; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_B; @@ -258,7 +258,7 @@ void multipleViaPoints() { ) ); - data.mcCostParamsBuilder().transferCost(100); + data.withTransferCost(100); var requestBuilder = prepareRequest(); diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java similarity index 90% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java index f03eb5335a0..4a08634db93 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java @@ -8,7 +8,6 @@ import static org.opentripplanner.raptor._data.api.PathUtils.pathsToString; import static org.opentripplanner.raptor._data.transit.TestAccessEgress.walk; import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_A; import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_B; @@ -19,13 +18,14 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.raptor.RaptorService; import org.opentripplanner.raptor._data.transit.TestTransitData; +import org.opentripplanner.raptor._data.transit.TestTripPattern; import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** * FEATURE UNDER TEST @@ -54,13 +54,13 @@ public class K01_TransitPriorityTest { @BeforeEach private void prepareRequest() { data.withRoutes( - route(pattern("L1", STOP_B, STOP_C).withPriorityGroup(GROUP_A)) + route(TestTripPattern.of("L1", STOP_B, STOP_C).priorityGroup(GROUP_A).build()) .withTimetable(schedule("00:02 00:12")), - route(pattern("U1", STOP_B, STOP_C).withPriorityGroup(GROUP_A)) + route(TestTripPattern.of("U1", STOP_B, STOP_C).priorityGroup(GROUP_A).build()) .withTimetable(schedule("00:02 00:12:01")), - route(pattern("L2", STOP_B, STOP_C).withPriorityGroup(GROUP_B)) + route(TestTripPattern.of("L2", STOP_B, STOP_C).priorityGroup(GROUP_B).build()) .withTimetable(schedule("00:02 00:13")), - route(pattern("L3", STOP_B, STOP_C).withPriorityGroup(GROUP_C)) + route(TestTripPattern.of("L3", STOP_B, STOP_C).priorityGroup(GROUP_C).build()) .withTimetable(schedule("00:02 00:14")) ); diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java similarity index 90% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java index fb21128728c..e6d1fc38d12 100644 --- a/application/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java @@ -11,7 +11,6 @@ import static org.opentripplanner.raptor._data.api.PathUtils.pathsToString; import static org.opentripplanner.raptor._data.transit.TestAccessEgress.walk; import static org.opentripplanner.raptor._data.transit.TestRoute.route; -import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_A; import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_B; @@ -22,13 +21,14 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.raptor.RaptorService; import org.opentripplanner.raptor._data.transit.TestTransitData; +import org.opentripplanner.raptor._data.transit.TestTripPattern; import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** * FEATURE UNDER TEST @@ -56,13 +56,13 @@ private void prepareRequest() { // they are in different groups), but not L3 (which certainly is in its own group but // its cost is outside the range allowed by the slack). data.withRoutes( - route(pattern("L1", STOP_B, STOP_C).withPriorityGroup(GROUP_A)) + route(TestTripPattern.of("L1", STOP_B, STOP_C).priorityGroup(GROUP_A).build()) .withTimetable(schedule("00:02 00:12")), - route(pattern("U1", STOP_B, STOP_D).withPriorityGroup(GROUP_A)) + route(TestTripPattern.of("U1", STOP_B, STOP_D).priorityGroup(GROUP_A).build()) .withTimetable(schedule("00:02 00:12:01")), - route(pattern("L2", STOP_B, STOP_E).withPriorityGroup(GROUP_B)) + route(TestTripPattern.of("L2", STOP_B, STOP_E).priorityGroup(GROUP_B).build()) .withTimetable(schedule("00:02 00:13")), - route(pattern("L3", STOP_B, STOP_F).withPriorityGroup(GROUP_C)) + route(TestTripPattern.of("L3", STOP_B, STOP_F).priorityGroup(GROUP_C).build()) .withTimetable(schedule("00:02 00:14")) ); diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyAccessTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyAccessTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyAccessTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyAccessTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyEgressTest.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyEgressTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyEgressTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/L01_TimePenaltyEgressTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/images/F11.svg b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/F11.svg similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/images/F11.svg rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/F11.svg diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/images/F12.svg b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/F12.svg similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/images/F12.svg rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/F12.svg diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/images/Samples.excalidraw b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/Samples.excalidraw similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/images/Samples.excalidraw rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/images/Samples.excalidraw diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/package-info.md b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/package-info.md similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/package-info.md rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/package-info.md diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/ExpectedList.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/ExpectedList.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/ExpectedList.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/ExpectedList.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/ModuleTestDebugLogging.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/ModuleTestDebugLogging.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/ModuleTestDebugLogging.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/ModuleTestDebugLogging.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCase.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCase.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCase.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCase.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCaseFactory.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCaseFactory.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCaseFactory.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestCaseFactory.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfig.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfig.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfig.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfig.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfigSetBuilder.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfigSetBuilder.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfigSetBuilder.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/RaptorModuleTestConfigSetBuilder.java diff --git a/application/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java b/raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java rename to raptor/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java diff --git a/application/src/test/java/org/opentripplanner/raptor/package-info.md b/raptor/src/test/java/org/opentripplanner/raptor/package-info.md similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/package-info.md rename to raptor/src/test/java/org/opentripplanner/raptor/package-info.md diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/internalapi/NoopPassThroughPointsServiceTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/internalapi/NoopPassThroughPointsServiceTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/internalapi/NoopPassThroughPointsServiceTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/internalapi/NoopPassThroughPointsServiceTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalStateParetoSetTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalStateParetoSetTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalStateParetoSetTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/StopArrivalStateParetoSetTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactoryTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactoryTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactoryTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactoryTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivalTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrivalTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrivalTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrivalTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/AccessStopArrivalTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/StopArrivalFactoryC1Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrivalTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrivalTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrivalTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransferStopArrivalTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrivalTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrivalTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrivalTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c1/TransitStopArrivalTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/AccessStopArrivalC2Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/StopArrivalFactoryC2Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransferStopArrivalC2Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/c2/TransitStopArrivalC2Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/passthrough/BitSetPassThroughPointsServiceTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c1/PatternRideC1Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2Test.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2Test.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2Test.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/PatternRideC2Test.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/DestinationArrivalTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java similarity index 69% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java index bac2c1b0431..027d3d5852a 100644 --- a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathMapperTest.java @@ -1,38 +1,38 @@ package org.opentripplanner.raptor.rangeraptor.path; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAForwardSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAReverseSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAText; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAWithOpeningHoursForwardSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAWithOpeningHoursReverseSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseAWithOpeningHoursText; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBForwardSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBReverseSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBText; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBWithOpeningHoursForwardSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBWithOpeningHoursReverseSearch; +import static org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase.flexCaseBWithOpeningHoursText; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.BASIC_PATH_AS_DETAILED_STRING; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.C1_CALCULATOR; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.basicTripByForwardSearch; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.basicTripByReverseSearch; import static org.opentripplanner.raptor._data.stoparrival.BasicPathTestCase.lifeCycle; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAForwardSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAReverseSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAText; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAWithOpeningHoursForwardSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAWithOpeningHoursReverseSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseAWithOpeningHoursText; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBForwardSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBReverseSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBText; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBWithOpeningHoursForwardSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBWithOpeningHoursReverseSearch; -import static org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase.flexCaseBWithOpeningHoursText; import org.junit.jupiter.api.Test; import org.opentripplanner.raptor._data.RaptorTestConstants; -import org.opentripplanner.raptor._data.stoparrival.FlexAccessAndEgressPathTestCase; +import org.opentripplanner.raptor._data.stoparrival.AccessAndEgressWithOpeningHoursPathTestCase; +import org.opentripplanner.raptor._data.transit.TestCostCalculator; import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; import org.opentripplanner.raptor.spi.RaptorSlackProvider; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; public class PathMapperTest implements RaptorTestConstants { private static final RaptorSlackProvider FLEX_SLACK_PROVIDER = - FlexAccessAndEgressPathTestCase.SLACK_PROVIDER; - private static final DefaultCostCalculator FLEX_COST_CALCULATOR = - FlexAccessAndEgressPathTestCase.C1_CALCULATOR; + AccessAndEgressWithOpeningHoursPathTestCase.SLACK_PROVIDER; + private static final TestCostCalculator FLEX_COST_CALCULATOR = + AccessAndEgressWithOpeningHoursPathTestCase.C1_CALCULATOR; /* BASIC CASES */ diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java similarity index 99% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java index 296103499a1..00558b6f63d 100644 --- a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparatorsTest.java @@ -21,7 +21,6 @@ import org.opentripplanner.raptor.api.model.GeneralizedCostRelaxFunction; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.model.RelaxFunction; -import org.opentripplanner.raptor.api.model.SearchDirection; import org.opentripplanner.raptor.api.path.RaptorPath; import org.opentripplanner.raptor.rangeraptor.internalapi.ParetoSetCost; import org.opentripplanner.raptor.rangeraptor.internalapi.ParetoSetTime; diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheckTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheckTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheckTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/standard/besttimes/SimpleArrivedAtDestinationCheckTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivalsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivalsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivalsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/support/IntArraySingleCriteriaArrivalsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPathsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPathsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPathsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPathsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenaltyTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenaltyTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenaltyTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessWithPenaltyTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenaltyTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenaltyTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenaltyTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressWithPenaltyTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardRaptorTransitCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTimeCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ForwardTransitCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RaptorSearchWindowCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseRaptorTransitCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTimeCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculatorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculatorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculatorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/ReverseTransitCalculatorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTrackerTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTrackerTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTrackerTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/RoundTrackerTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapterTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapterTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapterTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/SlackProviderAdapterTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearchTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearchTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearchTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripScheduleExactMatchSearchTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearchTest.java b/raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearchTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearchTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/TripTimesSearchTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/service/HeuristicToRunResolverTest.java b/raptor/src/test/java/org/opentripplanner/raptor/service/HeuristicToRunResolverTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/service/HeuristicToRunResolverTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/service/HeuristicToRunResolverTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java b/raptor/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java similarity index 87% rename from application/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java index 0ae5a09aa18..76c05386f18 100644 --- a/application/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java +++ b/raptor/src/test/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEventTest.java @@ -15,6 +15,6 @@ void testEmptyBoardOrAlightEvent() { assertTrue(subject.empty()); assertEquals(300, subject.earliestBoardTime()); assertEquals(RaptorTransferConstraint.REGULAR_TRANSFER, subject.transferConstraint()); - assertEquals("EmptyBoardOrAlightEvent[earliestBoardTime=300]", subject.toString()); + assertEquals("EmptyBoardOrAlightEvent(00:05:00)", subject.toString()); } } diff --git a/application/src/test/java/org/opentripplanner/raptor/spi/RaptorSlackProviderTest.java b/raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorSlackProviderTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/spi/RaptorSlackProviderTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorSlackProviderTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/spi/RaptorTripPatternTest.java b/raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorTripPatternTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/spi/RaptorTripPatternTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorTripPatternTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/spi/RaptorTripScheduleTest.java b/raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorTripScheduleTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/spi/RaptorTripScheduleTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/spi/RaptorTripScheduleTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/spi/UnknownPathTest.java b/raptor/src/test/java/org/opentripplanner/raptor/spi/UnknownPathTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/spi/UnknownPathTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/spi/UnknownPathTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/BitSetIteratorTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/BitSetIteratorTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/BitSetIteratorTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/BitSetIteratorTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/IntIteratorsTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/IntIteratorsTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/IntIteratorsTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/IntIteratorsTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/PathStringBuilderTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/PathStringBuilderTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/PathStringBuilderTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/PathStringBuilderTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/composite/CompositeUtilTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/composite/CompositeUtilTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/composite/CompositeUtilTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/composite/CompositeUtilTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerCompositeTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerCompositeTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerCompositeTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerCompositeTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetEventListenerTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarkerTest.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarkerTest.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarkerTest.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/ParetoSetWithMarkerTest.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/TestParetoSetEventListener.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/TestParetoSetEventListener.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/TestParetoSetEventListener.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/TestParetoSetEventListener.java diff --git a/application/src/test/java/org/opentripplanner/raptor/util/paretoset/TestVector.java b/raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/TestVector.java similarity index 100% rename from application/src/test/java/org/opentripplanner/raptor/util/paretoset/TestVector.java rename to raptor/src/test/java/org/opentripplanner/raptor/util/paretoset/TestVector.java diff --git a/utils/src/main/java/org/opentripplanner/utils/collection/ListUtils.java b/utils/src/main/java/org/opentripplanner/utils/collection/ListUtils.java index c2f263d0044..658bf768179 100644 --- a/utils/src/main/java/org/opentripplanner/utils/collection/ListUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/collection/ListUtils.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Set; import java.util.function.Function; +import javax.annotation.Nullable; public class ListUtils { @@ -75,7 +76,7 @@ public static List ofNullable(T input) { * {@code null} an empty collection is returned. If not the {@link List#copyOf(Collection)} is * called. */ - public static List nullSafeImmutableList(Collection c) { + public static List nullSafeImmutableList(@Nullable Collection c) { return (c == null) ? List.of() : List.copyOf(c); } }